diff --git a/.travis.yml b/.travis.yml index 97051215c..0abd65fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ jobs: - perl: "5.32.0" env: TARGET=PostgreSQL ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_postgresql_backend_config.ini services: postgresql - # Cover all Perl versions with SQLite + # Cover supported Perl versions with SQLite - perl: "5.32.0" env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini - perl: "5.30" @@ -18,12 +18,6 @@ jobs: env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini - perl: "5.26" env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini - - perl: "5.24" - env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini - - perl: "5.22" - env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini - - perl: "5.16" - env: TARGET=SQLite ZONEMASTER_RECORD=0 ZONEMASTER_BACKEND_CONFIG_FILE=./share/travis_sqlite_backend_config.ini addons: apt: @@ -107,19 +101,20 @@ before_install: - git clone --depth=1 --branch=$TRAVIS_BRANCH https://github.com/zonemaster/zonemaster-engine.git - ( cd zonemaster-engine && cpanm --verbose --notest . ) && rm -rf zonemaster-engine + # Install share files + - mkdir -p ./lib/auto/share/dist/ + - ln -s ../../../../share ./lib/auto/share/dist/Zonemaster-Backend + before_script: - - if [[ "$TARGET" == "PostgreSQL" ]]; then psql -c "create user travis_zonemaster WITH PASSWORD 'travis_zonemaster';" -U postgres; fi - - if [[ "$TARGET" == "PostgreSQL" ]]; then psql -c 'create database travis_zonemaster OWNER travis_zonemaster;' -U postgres; fi + - if [[ "$TARGET" == "PostgreSQL" ]]; then psql -U postgres -c "CREATE USER travis_zonemaster WITH PASSWORD 'travis_zonemaster';"; fi + - if [[ "$TARGET" == "PostgreSQL" ]]; then psql -U postgres -c 'CREATE DATABASE travis_zonemaster OWNER travis_zonemaster;'; fi - if [[ "$TARGET" == "PostgreSQL" ]]; then cpanm DBD::Pg; fi - - if [[ "$TARGET" == "PostgreSQL" ]]; then perl -I./lib ./script/create_db_postgresql_9.3.pl; fi - - if [[ "$TARGET" == "MySQL" ]]; then mysql -e "CREATE USER 'travis_zm'@'localhost' IDENTIFIED BY 'travis_zonemaster';" -u root; fi - - if [[ "$TARGET" == "MySQL" ]]; then mysql -e "CREATE DATABASE travis_zonemaster CHARACTER SET utf8 COLLATE utf8_bin;" -u root; fi - - if [[ "$TARGET" == "MySQL" ]]; then mysql -e "GRANT ALL ON travis_zonemaster.* TO 'travis_zm'@'localhost';" -u root; fi - - if [[ "$TARGET" == "MySQL" ]]; then mysql -e "FLUSH PRIVILEGES;" -u root; fi - - if [[ "$TARGET" == "MySQL" ]]; then cpanm --force DBD::mysql; fi - - if [[ "$TARGET" == "MySQL" ]]; then perl -I./lib ./script/create_db_mysql.pl; fi + - if [[ "$TARGET" == "MySQL" ]]; then mysql -u root -e "CREATE USER 'travis_zm'@'localhost' IDENTIFIED BY 'travis_zonemaster';"; fi + - if [[ "$TARGET" == "MySQL" ]]; then mysql -u root -e "CREATE DATABASE travis_zonemaster CHARACTER SET utf8 COLLATE utf8_bin;"; fi + - if [[ "$TARGET" == "MySQL" ]]; then mysql -u root -e "GRANT ALL ON travis_zonemaster.* TO 'travis_zm'@'localhost';"; fi + - if [[ "$TARGET" == "MySQL" ]]; then cpanm DBD::mysql; fi script: + - perl -I./lib ./share/create_db.pl - perl Makefile.PL && make test - diff --git a/Changes b/Changes index 5750a76e5..bef41d545 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,47 @@ Release history for Zonemaster component Zonemaster-Backend +v8.0.0 2021-12-03 (public release version) + + [Breaking changes] + - Removes support of PostgreSQL before version 10 (#892) + - Removes "retry" configuration setting (#896, 881) + + [Deprecation] + - Deprecates country code in RPC API calls (#796, #794) + + [Features] + - Adds translation of error messages (#891) + - Adds Finnish translation of error messages (#921, #911) + - Adds Swedish translation of error messages (#899) + - Replaces CentOS with Rocky Linux (#908, #895) + - Adds upgrade documentation (#907, #879, #618) + - Adds support for collecting metrics (#844) + - Optimization of batches when PostgreSQL is used (#890) + - Improved result history response in API (#837, #830) + - Makes database port configurable (#755, #496) + + [Fixes] + - Document clean-up and improvement (#913, #907, #882, #873, #871, + #822, #814, #816, #797) + - Improves error catching (#916, #914) + - Database clean-up and improvement (#906, #887, #859, #833, #839, #831, + #815, #824, #826, #812, #798, #775, #804, #805, 806) + - Updates zmb command line tool (#825, #628, #810, #768, #787, #780) + - Updates dependencies (#902, #901, 903) + - Fix translation issue (#894, #811, #809) + - Improves error message when creation of API user is blocked (#889) + - Improves RPC API error messages (#853, #789, #847, #819, #817, #820, + #703) + - Harmonize database code (#841, #832, #840, #865, #834, #689, #805) + - Use SQLite by default (#855) + - Improves handling of crashed tests (#845) + - Fixes configuration loading error (#851, #813) + - Improves log handling (#843) + - Fixes local adress mapping (#836) + - Updates default location for configuration file (#835) + - Improves validation (#801, #808, #685, #808, #802, #799, #757) + + v7.0.0 2021-09-15 (public security release) [Fixes] - By design adding a API user (needed for the batch function) is limited to diff --git a/LICENSE b/LICENSE index 7dec8a8b2..334ff0587 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,11 @@ ### Code license -Copyright (c) 2013-2017, IIS (The Internet Foundation in Sweden) -Copyright (c) 2013-2017, AFNIC +Copyright (c) The Swedish Internet Foundation () +Copyright (c) AFNIC () All rights reserved. +Copyright belongs to external contributor where applicable. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -28,8 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### Documentation license -Copyright (c) 2013-2017, IIS (The Internet Foundation in Sweden) -Copyright (c) 2013-2017, AFNIC +Copyright (c) The Swedish Internet Foundation () +Copyright (c) AFNIC () +All rights reserved. + +Copyright belongs to external contributor where applicable. + Creative Commons Attribution 4.0 International License You should have received a copy of the license along with this diff --git a/MANIFEST b/MANIFEST index 414f85a5e..2ab319747 100644 --- a/MANIFEST +++ b/MANIFEST @@ -6,11 +6,14 @@ docs/Configuration.md docs/files-description.md docs/GettingStarted.md docs/Installation.md +docs/Telemetry.md docs/TypographicConventions.md -docs/upgrade_db_zonemaster_backend_ver_1.0.3.md -docs/upgrade_db_zonemaster_backend_ver_1.1.0.md -docs/upgrade_db_zonemaster_backend_ver_5.0.0.md -docs/upgrade_db_zonemaster_backend_ver_5.0.2.md +docs/Upgrade.md +docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md +docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md +docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md +docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md +docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm @@ -21,6 +24,7 @@ inc/Module/Install/Scripts.pm inc/Module/Install/Share.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm +inc/Module/Install/External.pm lib/Zonemaster/Backend.pm lib/Zonemaster/Backend/Config.pm lib/Zonemaster/Backend/Config/DCPlugin.pm @@ -28,6 +32,8 @@ lib/Zonemaster/Backend/DB.pm lib/Zonemaster/Backend/DB/MySQL.pm lib/Zonemaster/Backend/DB/PostgreSQL.pm lib/Zonemaster/Backend/DB/SQLite.pm +lib/Zonemaster/Backend/Errors.pm +lib/Zonemaster/Backend/Metrics.pm lib/Zonemaster/Backend/RPCAPI.pm lib/Zonemaster/Backend/TestAgent.pm lib/Zonemaster/Backend/Translator.pm @@ -38,8 +44,6 @@ MANIFEST This list of files META.yml README.md script/add-batch-job.pl -script/create_db_mysql.pl -script/create_db_postgresql_9.3.pl script/zmb script/zmtest script/zonemaster_backend_rpcapi.psgi @@ -47,16 +51,17 @@ script/zonemaster_backend_testagent share/backend_config.ini share/cleanup-mysql.sql share/cleanup-postgres.sql -share/create_db_sqlite.pl +share/create_db.pl share/freebsd-pwd.conf -share/initial-mysql.sql -share/initial-postgres.sql -share/patch_db_README.txt -share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl -share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl -share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl -share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl -share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +share/patch/README.txt +share/patch/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl +share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl +share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl +share/patch/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl +share/patch/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl +share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl share/tmpfiles.conf share/travis_mysql_backend_config.ini share/travis_postgresql_backend_config.ini @@ -65,7 +70,17 @@ share/zm-rpcapi.lsb share/zm-testagent.lsb share/zm_rpcapi-bsd share/zm_testagent-bsd +share/locale/da/LC_MESSAGES/Zonemaster-Backend.mo +share/locale/es/LC_MESSAGES/Zonemaster-Backend.mo +share/locale/fi/LC_MESSAGES/Zonemaster-Backend.mo +share/locale/fr/LC_MESSAGES/Zonemaster-Backend.mo +share/locale/nb/LC_MESSAGES/Zonemaster-Backend.mo +share/locale/sv/LC_MESSAGES/Zonemaster-Backend.mo +share/Makefile +share/GNUmakefile t/config.t +t/db.t +t/parameters_validation.t t/test01.data t/test01.t t/test_validate_syntax.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index e48ce15e3..c249ee5c7 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -8,6 +8,10 @@ ^\.github/pull_request_template\.md$ ^\.travis\.yml$ ^docs/internal-documentation/ +\.po$ +^share/[^/]*\.mo$ +^share/Zonemaster-Backend.pot$ +^share/update-po$ #!start included /usr/share/perl/5.20/ExtUtils/MANIFEST.SKIP # Avoid version control files. diff --git a/Makefile.PL b/Makefile.PL index f737f5f3f..fe902a07f 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,4 +1,5 @@ use inc::Module::Install; +use Module::Install::Share; name 'Zonemaster-Backend'; all_from 'lib/Zonemaster/Backend.pm'; @@ -8,6 +9,8 @@ bugtracker 'https://github.com/zonemaster/zonemaster-backend/issues'; # "2.1.0" could be declared as "2.001" but not as "2.1" # (see Zonemaster::LDNS below) +configure_requires 'Locale::Msgfmt' => 0.15; + requires 'Class::Method::Modifiers' => 1.09, 'Config::IniFiles' => 0, @@ -18,11 +21,12 @@ requires 'HTML::Entities' => 0, 'JSON::PP' => 0, 'JSON::RPC' => 1.01, - 'JSON::Validator' => 3.12, + 'JSON::Validator' => 4.00, 'Log::Any' => 0, 'Log::Any::Adapter::Dispatch' => 0, 'Log::Dispatch' => 0, 'LWP::UserAgent' => 0, + 'Mojolicious' => 7.28, 'Moose' => 2.04, 'Parallel::ForkManager' => 1.12, 'Plack::Builder' => 0, @@ -31,9 +35,10 @@ requires 'Starman' => 0, 'String::ShellQuote' => 0, 'Try::Tiny' => 0.12, - 'Zonemaster::Engine' => 4.002, + 'Zonemaster::Engine' => 4.003, 'Zonemaster::LDNS' => 2.002, 'Plack::Middleware::ReverseProxy' => 0, + 'Locale::TextDomain' => 1.20, ; test_requires 'DBD::SQLite'; @@ -53,4 +58,26 @@ install_script 'zmb'; no_index directory => 'CodeSnippets'; no_index directory => 'Doc'; +# Make all platforms include inc/Module/Install/External.pm +requires_external_bin 'find'; +if ($^O eq "freebsd") { + requires_external_bin 'gmake'; +}; + +sub MY::postamble { + my $text; + if ($^O eq "freebsd") { + # Make FreeBSD use gmake for share/Makefile + $text = 'GMAKE ?= "gmake"' . "\n" + . 'pure_all :: share/Makefile' . "\n" + . "\t" . 'cd share && $(GMAKE) all' . "\n"; + } else { + $text = 'pure_all :: share/Makefile' . "\n" + . "\t" . 'cd share && $(MAKE) all' . "\n"; + }; + return $text; +}; + +install_share; + WriteAll; diff --git a/README.md b/README.md index 25565d31a..74f64abf6 100644 --- a/README.md +++ b/README.md @@ -25,28 +25,10 @@ Zonemaster Engine installed. Please see the instructions](https://github.com/zonemaster/zonemaster-engine/blob/master/docs/Installation.md). -### Upgrade +### Upgrade -If you upgrade Zonemaster-Backend and want to keep the content of the database -(MySQL/MariaDB or PostgrSQL) then you should not reset the database when you -follow the [installation instructions]. In some cases you need to patch the -database when you update Zonemaster-Backend. +See the [upgrade document]. -Always take a backup first if the database is valuable. - -Current version | Link to instructions | Comments -------------------------------------|-----------------------|----------------------- -Older than 1.0.3 | [Upgrade to 1.0.3] | -At least 1.0.3 but older than 1.1.0 | [Upgrade to 1.1.0] | -At least 1.1.0 but older than 5.0.0 | [Upgrade to 5.0.0] | -At least 5.0.0 but older than 5.0.2 | [Upgrade to 5.0.2] | For MySQL/MariaDB only - -If the database was created before Zonemaster-Backend version 5.0.0, then you -have to upgrade in several steps. - -To complete the upgrade follow the [installation instructions], except for creating -the database. If you instead want to start from an empty database, then you remove the database -and create a new database using the [installation instructions]. ### Installation @@ -83,7 +65,4 @@ The software is released under the 2-clause BSD license. See separate [Configuration documentation]: docs/Configuration.md [Installation instructions]: docs/Installation.md -[Upgrade to 1.0.3]: docs/upgrade_db_zonemaster_backend_ver_1.0.3.md -[Upgrade to 1.1.0]: docs/upgrade_db_zonemaster_backend_ver_1.1.0.md -[Upgrade to 5.0.0]: docs/upgrade_db_zonemaster_backend_ver_5.0.0.md -[Upgrade to 5.0.2]: docs/upgrade_db_zonemaster_backend_ver_5.0.2.md +[Upgrade document]: docs/Upgrade.md diff --git a/docs/API.md b/docs/API.md index f838d2f96..84fe43879 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,5 +1,50 @@ # API +## Table of contents + +* [Purpose](#purpose) +* [Protocol](#protocol) + * [Deviations from JSON-RPC 2.0](#deviations-from-json-rpc-20) + * [Notes on the JSON-RPC 2.0 implementation](#notes-on-the-json-rpc-20-implementation) +* [Request handling](#Request-handling) +* [Error reporting](#Error-reporting) +* [Privilege levels](#Privilege-levels) +* [Data types](#Data-types) + * [API key](#API-key) + * [Batch id](#Batch-id) + * [Client id](#Client-id) + * [Client version](#Client-version) + * [Domain name](#Domain-name) + * [DS info](#DS-info) + * [IP address](#IP-address) + * [Language tag](#Language-tag) + * [Name server](#Name-server) + * [Non-negative integer](#Non-negative-integer) + * [Priority](#Priority) + * [Profile name](#Profile-name) + * [Progress percentage](#Progress-percentage) + * [Queue](#Queue) + * [Severity level](#Severity-level) + * [Test id](#Test-id) + * [Test result](#Test-result) + * [Timestamp](#Timestamp) + * [Username](#Username) +* [API method: version_info](#API-method-version_info) +* [API method: profile_names](#API-method-profile_names) +* [API method: get_language_tags](#API-method-get_language_tags) +* [API method: get_host_by_name](#API-method-get_host_by_name) +* [API method: get_data_from_parent_zone](#API-method-get_data_from_parent_zone) +* [API method: start_domain_test](#API-method-start_domain_test) +* [API method: test_progress](#API-method-test_progress) +* [API method: get_test_results](#API-method-get_test_results) +* [API method: get_test_history](#API-method-get_test_history) + * [Undelegated and delegated](#undelegated-and-delegated) +* [API method: add_api_user](#API-method-add_api_user) +* [API method: add_batch_job](#API-method-add_batch_job) +* [API method: get_batch_job_result](#API-method-get_batch_job_result) +* [API method: get_test_params](#API-method-get_test_params) + + ## Purpose This document describes the JSON-RPC API provided by the Zonemaster *RPC API daemon*. @@ -9,13 +54,13 @@ Health checks are called *tests* in Zonemaster lingo. ## Protocol -This API is implemented using [JSON-RPC 2.0](http://www.jsonrpc.org/specification). +This API is implemented using [JSON-RPC 2.0]. JSON-RPC request objects are accepted in the body of HTTP POST requests to any path. The HTTP request must contain the header `Content-Type: application/json`. All JSON-RPC request and response objects have the keys `"jsonrpc"`, `"id"` and `"method"`. -For details on these, refer to the JSON-RPC 2.0 specification. +For details on these, refer to the [JSON-RPC 2.0] specification. ### Deviations from JSON-RPC 2.0 @@ -28,7 +73,7 @@ For details on these, refer to the JSON-RPC 2.0 specification. ### Notes on the JSON-RPC 2.0 implementation * Extra top-level properties in request objects are allowed but ignored. -* Extra properties in the `"params"` object are allowed for some methods but ignored for others. +* No extra properties are allowed in the `"params"` object. * Error messages from the API should be considered sensitive as they sometimes leak details about the internals of the application and the system. * The error code -32601 is used when the `"method"` property is missing, rather than the perhaps expected error code -32600. @@ -55,6 +100,11 @@ If the request object is invalid JSON, an error with code `-32700` is reported. If no method is specified or an invalid method is specified, an error with code `-32601` is reported. +If no `params` object is specified when it is required, or the `params` object +for the specified method is invalid, an error with code `-32602` is reported. +For more information on the validation error data format see +[Validation error data]. + All error states that occur after the RPC method has been identified are reported as internal errors with code `-32603`. @@ -63,8 +113,10 @@ All error states that occur after the RPC method has been identified are reporte This API provides three classes of methods: * *Unrestricted* methods are available to anyone with access to the API. -* *Authenticated* methods have parameters for username and API key credentials. -* *Administrative* methods require that the connection to the API is opened from localhost (`127.0.0.1` or `::1`). +* *Authenticated* methods have parameters for *username* and *api key* + credentials. +* *Administrative* methods require that the connection to the API is opened from + localhost (`127.0.0.1` or `::1`). ## Data types @@ -88,7 +140,7 @@ Represents the password of an authenticated account (see *[Privilege levels]*) Basic data type: number -A positive integer. +A strictly positive integer. The unique id of a *batch*. @@ -136,24 +188,62 @@ Basic data type: string Basic data type: object -DS for [Delegation Signer](https://tools.ietf.org/html/rfc4034) references DNSKEY-records in the sub-delegated zone. +DS for [Delegation Signer] references a DNSKEY record in the delegated zone. Properties: -* `"digest"`: A string, required. Either 40 or 64 hexadecimal characters (case insensitive). +* `"digest"`: A string, required. Either 40, 64 or 96 hexadecimal characters (case insensitive). * `"algorithm"`: An non negative integer, required. * `"digtype"`: An non negative integer, required. * `"keytag"`: An non negative integer, required. -Extra properties in *DS info* objects are ignored when present in RPC method arguments, and never returned as part of RPC method results. - ### IP address Basic data type: string -This parameter is a string that are an IPv4 or IPv6. It's validated with the following regexes: - - IPv4 : `/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/` - - IPv6 : `/^([0-9A-Fa-f]{1,4}:[0-9A-Fa-f:]{1,}(:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?)|([0-9A-Fa-f]{1,4}::[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/` +This parameter is a string that is either + - a valid IPv4 address in [dot-decimal notation] ; + - a valid IPv6 address in [recommended text format][RFC 5952] for IPv6 addresses. + +### Language tag + +Basic data type: string + +A string matching one of the following regular expression: +* `/^[a-z]{2}$/`, preferred format. +* `/^[a-z]{2}_[A-Z]{2}$/`, **deprecated** format, use the preferred format instead. + +The set of valid *language tags* is further constrained by the +[LANGUAGE.locale] property. +* If the *language tag* is a five character string, it needs to match a *locale + tag* in [LANGUAGE.locale]. +* If the *language tag* is a two-character string, it needs to match the + first two characters of exactly one *locale tag* in [LANGUAGE.locale]. + (So that it is unambiguous which *locale tag* is matched.) + +E.g. if [LANGUAGE.locale] is "en_US en_UK sv_SE", all the valid *language tags* +are "en_US", "en_UK", "sv_SE" and "sv". + +The use of `language tags` that include the country code is *deprecated*. + +#### Design + +The two first characters of the *language tag* are intended to be an +[ISO 639-1] two-character language code and the optional two last characters +are intended to be an [ISO 3166-1 alpha-2] two-character country code. + +#### Out-of-the box support + +A default installation will accept the following *language tags*: + +Language | Preferred language tag | Deprecated language tag +---------|------------------------|------------------ +Danish | da | da_DK +English | en | en_US +Finnish | fi | fi_FI +French | fr | fr_FR +Norwegian| nb | nb_NO +Swedish | sv | sv_SE ### Name server @@ -166,6 +256,13 @@ Properties: * `"ip"`: An *IP address* (IPv4 or IPv6), optional. (default: unset) +### Non-negative integer + +Basic data type: number (integer) + +A non-negative integer is either zero or strictly positive. + + ### Priority Basic data type: number (integer) @@ -179,22 +276,57 @@ The drawback of this setup will be that the GUI will have to wait for at least o Basic data type: string This parameter is a case-insensitive string validated with the case-insensitive -regex `/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$/i`. +regex `/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$/i` which must be predefined +in the configuration file as specified in the Configuration document +[profile sections]. The name of a [*profile*](Architecture.md#profile). -When a method received an unknown *profile name* value for in parameter with this type, it returns the following error message: +Below are the current error messages for an incorrect *profile name*. The +messages should, however, considered to be unstable and are planned to be updated +to gain consistent error messages from the RPCAPI. + +When a method receives an illegal *profile name* value for a parameter with this +type, it returns the following error message: ```json { - "jsonrpc": "2.0", - "id": 1, - "result": { - "message": "Invalid profile option format", - "status": "nok" + "jsonrpc":"2.0", + "id":1, + "error":{ + "message":"Invalid method parameter(s).", + "data": [ + { + "path": "/profile", + "message": "String does not match (?^ui:^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$)." + }, + ], + "code":"-32602" + } +} +``` + +When a method receives a legal but undefined *profile name* value for a parameter +with this type, it returns the following error message: + +```json +{ + "jsonrpc":"2.0", + "id":1, + "error":{ + "message":"Invalid method parameter(s).", + "data": [ + { + "path": "/profile", + "message": "Unknown profile" + }, + ], + "code":"-32602" } } ``` +The error code is "009" (as above) if method [start_domain_test] was requested. +Instead it will be "015" if method [add_batch_job] is requested. ### Progress percentage @@ -218,13 +350,20 @@ Basic data type: string One of the strings (in order from least to most severe): -* `"DEBUG"` * `"INFO"` * `"NOTICE"` * `"WARNING"` * `"ERROR"` * `"CRITICAL"` +Severity levels in Zonemaster are defined in the [Severity Level Definitions] +document. The following severity levels are not available through the RPCAPI +(in order from least to most severe): + +* DEBUG3 +* DEBUG2 +* DEBUG + ### Test id @@ -247,7 +386,7 @@ The object has three keys, `"module"`, `"message"` and `"level"`. Sometimes additional keys are present. -* `"ns"`: a *domain name*. The name server used by the *test module*. +* `"ns"`: a *domain name*. The name server used by the *test module*. This key is added when the module name is `"NAMESERVER"`. @@ -258,42 +397,6 @@ Basic data type: string Default database timestamp format: "Y-M-D H:M:S.ms". Example: "2017-12-18 07:56:17.156939" - -### Language tag - -Basic data type: string - -A string of A-Z, a-z and underscores matching the regular expression -`/^[a-z]{2}(_[A-Z]{2})?$/`. - -The `language tag` must match a `locale tag` in the configuration file. -If the `language tag` is a two-character string, it only needs to match the -first two characters of the `locale tag` from the configuration file, if -that is unique (there is only one `locale tag` starting with the same two -characters), else it is an error. - -Any other string is an error. - -The two first characters of the `language tag` are intended to be an -[ISO 639-1] two-character language code and the optional two last characters -are intended to be an [ISO 3166-1 alpha-2] two-character country code. - -A default installation will accept the following `language tags`: -* `da` or `da_DK` for Danish language. -* `en` or `en_US` for English language. -* `fi` or `fi_FI` for Finnish language. -* `fr` or `fr_FR` for French language. -* `nb` or `nb_NO` for Norwegian language. -* `sv` or `sv_SE` for Swedish language. - - -### Unsigned integer - -Basic data type: number (integer) - -An unsigned integer is either positive or zero. - - ### Username Basic data type: string @@ -304,6 +407,16 @@ I.e. a string matching `/^[a-zA-Z0-9-.@]{1,50}$/`. Represents the name of an authenticated account (see *[Privilege levels]*) +### Validation error data + +Basic data type: array + +The items of the array are objects with two keys, `"path"` and `"message"`: +* `"path"`: a string. A [JSON Pointer] to an element in the request's param + object. E.g.: `"/nameservers/0/ip"`. +* `"message"`: a string. The error message associated with the element + referenced by `"path"`. + ## API method: `version_info` @@ -348,7 +461,8 @@ An object with the following properties: ## API method: `profile_names` -Returns the names of the public subset of the [available profiles]. +Returns the names of the public subset of the +[available profiles][Profile sections]. Example request: ```json @@ -379,8 +493,13 @@ An array of *Profile names* in lower case. `"default"` is always included. ## API method: `get_language_tags` -Returns all valid [language tags][language tag] generated from the setting in -the configuration file. +Returns the set of valid [*language tags*][Language tag]. + +> Note: If there are two [*locale tags*][LANGUAGE.locale] in [LANGUAGE.locale] +> that would give the same [short language tag][Language tag] then the short tag +> is excluded from the set of valid [*language tags*][Language tag]. +> +> Note: Language tags that include country code are *deprecated*. Example request: ```json @@ -397,10 +516,16 @@ Example response: "jsonrpc": "2.0", "id": 1, "result": [ + "da", + "da_DK", "en", "en_US", + "fi", + "fi_FI", "fr", "fr_FR", + "nb", + "nb_NO", "sv", "sv_SE" ] @@ -460,7 +585,7 @@ Example response: An object with the property: -`"hostname"`: A *domain name*, required. The hostname whose IP addresses are to be resolved. +* `"hostname"`: A *domain name*, required. The hostname whose IP addresses are to be resolved. #### `"result"` @@ -477,10 +602,26 @@ value `0.0.0.0` if the lookup returned no A or AAAA records. #### `"error"` -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> +* If any parameter is invalid an error code of -32602 is returned. The `data` property contains an array of all errors, see [Validation error data]. + Example of error response: + +```json +{ + "error": { + "message": "Invalid method parameter(s).", + "code": "-32602", + "data": [ + { + "path": "/hostname", + "message": "Missing property" + } + ] + }, + "jsonrpc": "2.0", + "id": 1624630143271 +} +``` ## API method: `get_data_from_parent_zone` @@ -542,24 +683,42 @@ Example response: #### `"params"` -An object with the property: - -`"domain"`: A *domain name*, required. The domain whose DNS records are requested. +An object with the properties: +* `"domain"`: A *domain name*, required. The domain whose DNS records are requested. +* `"language"`: A [Language Tag], optional, used for validation error messages + translation, if not provided messages will be untranslated (in English). #### `"result"` An object with the following properties: -* `"ns_list"`: A list of *name server* objects representing the nameservers of the given *domain name*. -* `"ds_list"`: A list of *DS info* objects representing delegated signer of the given *domain name*. +* `"ns_list"`: A list of [*name server*][Name server] objects representing the nameservers of the given *domain name*. +* `"ds_list"`: A list of [*DS info*][DS info] objects representing delegation signer (DS record data) of the given *domain name*. #### `"error"` -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> +* If any parameter is invalid an error code of -32602 is returned. The `data` property contains an array of all errors, see [Validation error data]. + + Example of error response: + +```json +{ + "error": { + "data": [ + { + "message": "The domain name character(s) are not supported", + "path": "/domain" + } + ], + "code": "-32602", + "message": "Invalid method parameter(s)." + }, + "id": 1624630143271, + "jsonrpc": "2.0" +} +``` ## API method: `start_domain_test` @@ -615,38 +774,80 @@ Example response: An object with the following properties: * `"domain"`: A *domain name*, required. The zone to test. -* `"ipv6"`: A boolean, optional. (default `true`). Used to configure the test and enable IPv4 tests. -* `"ipv4"`: A boolean, optional. (default `true`). Used to configure the test and enable IPv6 tests. -* `"nameservers"`: A list of *name server* objects, optional. (default: `[]`). Used to perform un-delegated test. -* `"ds_info"`: A list of *DS info* objects, optional. (default: `[]`). Used to perform un-delegated test. -* `"profile"`: A *profile name*, optional. (default `"default"`). Run the tests using the given profile. -* `"config"`: **Deprecated**. A string, optional. Ignored. Specify `"profile"` instead. +* `"ipv6"`: A boolean, optional. (default: [`net.ipv4`][net.ipv4] profile value). Used to enable or disable testing over IPv4 transport protocol. +* `"ipv4"`: A boolean, optional. (default: [`net.ipv6`][net.ipv6] profile value). Used to enable or disable testing over IPv6 transport protocol. +* `"nameservers"`: A list of [*name server*][Name server] objects, optional. (default: `[]`). Used to perform un-delegated test. +* `"ds_info"`: A list of [*DS info*][DS info] objects, optional. (default: `[]`). Used to perform un-delegated test. +* `"profile"`: A [*profile name*][profile name], optional. (default: + `"default"`). Run the tests using the given profile. * `"client_id"`: A *client id*, optional. (default: unset). Used to monitor which client uses the API. * `"client_version"`: A *client version*, optional. (default: unset). Used to monitor which client use the API * `"priority"`: A *priority*, optional. (default: `10`) * `"queue"`: A *queue*, optional. (default: `0`) +* `"language"`: A [Language Tag], optional, used for validation error messages + translation, if not provided messages will be untranslated. -> > TODO: Clarify the purpose of each `"params"` property. > #### `"result"` -A *test id*. +A *test id*. -If the test has been run with the same domain name within an interval of 10 mins (hard coded), -then the new request does not trigger a new test, but returns with the results of the last test +If a test has been requested with the same parameters (as listed below) not more +than "reuse time" ago, then a new request will not trigger a new test. Instead +the `test id` of the previous test will be returned. The default value of +"reuse time" is 600 seconds, and can be set by the [`age_reuse_previous_test`] +key in the configuration file. +The parameters that are compared when to determine if two requests are to be +considered to be the same are `domain`, `ipv6`, `ipv4`, `nameservers`, `ds_info` +and `profile`. #### `"error"` -* If the given `profile` is not among the [available profiles], a user - error is returned. +* If any parameter is invalid an error code of -32602 is returned. + The `data` property contains an array of all errors, see [Validation error data]. + +* If the given `profile` is not among the [available profiles][Profile sections], + a user error is returned, see [profile name section][profile name]. + +Example of error response: + +```json +{ + "error": { + "code": "-32602", + "data": [ + { + "message": "Expected integer - got string.", + "path": "/ds_info/0/algorithm" + }, + { + "message": "Missing property.", + "path": "/ds_info/0/digest" + }, + { + "path": "/profile", + "message": "Unknown profile" + }, + { + "path": "/domain", + "message": "The domain name character(s) are not supported" + }, + { + "path": "/nameservers/0/ip", + "message": "Invalid IP address" + } + ], + "message": "Invalid method parameter(s)." + }, + "id": 1, + "jsonrpc": "2.0" +} +``` -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> ## API method: `test_progress` @@ -730,7 +931,6 @@ Example response: "domain": "zonemaster.net", "profile": "default", "ipv6": true, - "advanced": true, "nameservers": [ { "ns": "ns3.nic.se", @@ -784,19 +984,20 @@ In the case of a test created with `start_domain_test`: * `"creation_time"`: A *timestamp*. The time at which the *test* was enqueued. * `"id"`: An integer. -* `"hash_id"`: A *test id*. The *test* in question. -* `"params"`: The `"params"` object sent to `start_domain_test` when the *test* - was started. +* `"hash_id"`: A *test id*. The *test* in question. +* `"params"`: A normalized version `"params"` object sent to + `start_domain_test` when the *test* was started. * `"results"`: A list of *test result* objects. In the case of a test created with `add_batch_job`: * `"creation_time"`: A *timestamp*. The time at which the *test* was enqueued. * `"id"`: An integer. -* `"hash_id"`: A *test id*. The *test* in question. -* `"params"`: The `"params"` object sent to `start_domain_test` when the *test* - was started. -* `"results"`: the result is a list of *test id* corresponding to each tested domain. +* `"hash_id"`: A *test id*. The *test* in question. +* `"params"`: A normalized version `"params"` object sent to `add_batch_job` + when the *test* was started. +* `"results"`: the result is a list of *test id* corresponding to each tested + domain. > > TODO: Change name in the API of `"hash_id"` to `"test_id"` @@ -840,14 +1041,14 @@ Example response: { "id": "c45a3f8256c4a155", "creation_time": "2016-11-15 11:53:13.965982", + "undelegated": true, "overall_result": "error", - "advanced_options": null }, { "id": "32dd4bc0582b6bf9", + "undelegated": false, "creation_time": "2016-11-14 08:46:41.532047", "overall_result": "error", - "advanced_options": null }, ... ] @@ -860,13 +1061,18 @@ Example response: > symbol. > +### Undelegated and delegated + +A test is considered to be `"delegated"` below if the test was started, by +`start_domain_test` or `add_batch_job` without specifying neither `"nameserver"` +nor `"ds_info"`. Else it is considered to be `"undelegated"`. #### `"params"` An object with the following properties: -* `"offset"`: An *unsigned integer*, optional. (default: 0). Position of the first returned element from the database returned list. -* `"limit"`: An *unsigned integer*, optional. (default: 200). Number of element returned from the *offset* element. +* `"offset"`: A *non-negative integer*, optional. (default: 0). Position of the first returned element from the database returned list. +* `"limit"`: A *non-negative integer*, optional. (default: 200). Number of element returned from the *offset* element. * `"filter"`: A string, one of `"all"`, `"delegated"` and `"undelegated"`, optional. (default: `"all"`) * `"frontend_params"`: An object, required. @@ -881,16 +1087,17 @@ An object with the following properties: * `"id"` A *test id*. * `"creation_time"`: A *timestamp*. Time when the Test was enqueued. -* `"overall_result"`: A string. The most severe problem level logged in the test results. -It could be: - * `"ok"`, all is normal - * `"warning"`, equivalent to the `"WARNING"` *severity level*. - * `"error"`, equivalent to the `"ERROR"` *severity level*. - * `"critical"`, equivalent to the `"CRITICAL"` *severity level*. - - -> TODO: What about if the *test* was created with `add_batch_job` or something else? - +* `"overall_result"`: A string. It reflects the most severe problem level among + the test results for the test. It has one of the following values: + * `"ok"`, if there are only messages with *severity level* `"INFO"` or + `"NOTICE"`. + * `"warning"`, if there is at least one message with *severity level* + `"WARNING"`, but none with `"ERROR"` or `"CRITICAL"`. + * `"error"`, if there is at least one message with *severity level* + `"ERROR"`, but none with `"CRITICAL"`. + * `"critical"`, if there is at least one message with *severity level* + `"CRITICAL"`. +* `"undelegated"`: `true` if the test is undelegated, `false` otherwise. #### `"error"` @@ -901,13 +1108,13 @@ It could be: ## API method: `add_api_user` -In order to use advanced api features such as the *batch test*, it's necessaire to previously create an api key. -This key can be obtained with the creation of a user in the system. -This function allow the creation of a new user and so, the creation of a new api key. +In order to use the [`add_batch_job`](#API-method-add_batch_job) method a +*username* and its *api key* must be added by this method. -Add a new *user* +This method is not available if [`RPCAPI.enable_add_api_user`] is disabled +(disabled by default). This method is not available unless the connection to +RPCAPI is over localhost (*administrative* method). -This method requires the *administrative* *privilege level*. Example request: ```json @@ -936,8 +1143,9 @@ Example response: An object with the following properties: -* `"username"`: An *username*, required. The name of the user to add. -* `"api_key"`: An *api key*, required. The API key for the user to add. +* `"username"`: A *username*, required. The *username* to be added. +* `"api_key"`: An *api key*, required. The *api key* for the *username* to be + added. #### `"result"` @@ -946,6 +1154,7 @@ An integer. The value is equal to 1 if the registration is a success, or 0 if it #### `"error"` + > > TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. > @@ -953,29 +1162,89 @@ An integer. The value is equal to 1 if the registration is a success, or 0 if it Trying to add a already existing user: ```json { - "code": -32603, - "message": "User already exists\n" + "jsonrpc": "2.0", + "id": 1, + "error": { + "data": { + "username": "citron" + }, + "message": "User already exists", + "code": -32603 + } +} +``` + +Omitting params: +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": "-32602", + "message": "Invalid method parameter(s).", + "data": [ + { + "message": "Expected string - got null.", + "path": "/api_key" + } + ] + } +} +``` + +```json +{ + "error": { + "data": [ + { + "path": "/username", + "message": "Expected string - got null." + } + ], + "message": "Invalid method parameter(s).", + "code": "-32602" + }, + "jsonrpc": "2.0", + "id": 1 } ``` -Ommitting params: -```json +Trying to add a user over non-localhost: +```json { - "message": "username or api_key not provided to the method add_api_user\n", - "code": -32603 + "id": 1, + "jsonrpc": "2.0", + "error": { + "code": -32603, + "data": { + "remote_ip": "10.0.0.1" + }, + "message": "Call to \"add_api_user\" method not permitted from a remote IP" + } } ``` +Trying to add a user when the method is disabled: +```json +{ + "error": { + "code": -32601, + "message": "Procedure 'add_api_user' not found" + } +} +``` ## API method: `add_batch_job` -Add a run a new *batch test* composed by a set of *domain name* and a *params* object. +Add a new *batch test* composed by a set of *domain name* and a *params* object. All the domains will be tested using identical parameters. -An *api user* can only have one un-finished *batch* at a time. +This method is not available if [`RPCAPI.enable_add_batch_job`] is disabled +(enabled by default). -If an identical *test* for a domain was already enqueued and hasn't been started or was enqueued less than 10 minutes earlier, -no new *test* is enqueued for this domain. +A *username* and its *api key* can be added with the +[`add_api_user`](#API-method-add_api_user) method. A *username* can only have +one un-finished *batch* at a time. *Tests* enqueud using this method are assigned a *priority* of 5. @@ -1013,7 +1282,7 @@ Example response: An object with the following properties: -* `"username"`: An *username*, required. The name of the account of an authorized user. +* `"username"`: A *username*, required. The name of the account of an authorized user. * `"api_key"`: An *api key*, required. The api_key associated with the username. * `"domains"`: A list of *domain names*, required. The domains to be tested. * `"test_params"`: As described below, optional. (default: `{}`) @@ -1021,13 +1290,13 @@ An object with the following properties: The value of `"test_params"` is an object with the following properties: * `"client_id"`: A *client id*, optional. (default: unset) -* `"profile"`: A *profile name*, optional (default `"default"`). Run the tests using the given profile. -* `"config"`: **Deprecated.** A string, optional. Ignored. Specify profile instead. +* `"profile"`: A [*profile name*][profile name], optional (default: + `"default"`). Run the tests using the given profile. * `"client_version"`: A *client version*, optional. (default: unset) -* `"nameservers"`: A list of *name server* objects, optional. (default: `[]`) -* `"ds_info"`: A list of *DS info* objects, optional. (default: `[]`) -* `"ipv4"`: A boolean, optional. (default: `true`) -* `"ipv6"`: A boolean, optional. (default: `true`) +* `"nameservers"`: A list of [*name server*][Name server] objects, optional. (default: `[]`) +* `"ds_info"`: A list of [*DS info*][DS info] objects, optional. (default: `[]`) +* `"ipv6"`: A boolean, optional. (default: [`net.ipv4`][net.ipv4] profile value). +* `"ipv4"`: A boolean, optional. (default: [`net.ipv6`][net.ipv6] profile value). * `"priority"`: A *priority*, optional. (default: `5`) * `"queue"`: A *queue*, optional. (default: `0`) @@ -1039,15 +1308,53 @@ A *batch id*. #### `"error"` -* You can't create a new batch job. - A *batch* with unfinished *tests* already exists for this *api user*. -* If the given `profile` is not among the [available profiles], a user - error is returned. +* You cannot create a new batch job if a *batch* with unfinished *tests* already + exists for this *username*. +* If the given `profile` is not among the [available profiles][Profile sections], + a user error is returned, see the [profile name section][profile name]. +Trying to add a batch when a batch is still running for the *username* in the +request: +```json +{ + "jsonrpc": "2.0", + "error": { + "data": { + "creation_time": "2021-09-27 07:33:40", + "batch_id": 1 + }, + "code": -32603, + "message": "Batch job still running" + }, + "id": 1 +} -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> +``` + +Trying to add a batch when wrong *username* or *api key* is used: +```json +{ + "error": { + "message": "User not authorized to use batch mode", + "code": -32603, + "data": { + "username": "citron" + } + }, + "id": 1, + "jsonrpc": "2.0" +} +``` + +Trying to add a batch when the method has been disabled. +``` +{ + "error": { + "message": "Procedure 'add_batch_job' not found", + "code": -32601 + } +} +``` ## API method: `get_batch_job_result` @@ -1097,8 +1404,8 @@ An object with the property: An object with the following properties: -* `"nb_finished"`: an *unsigned integer*. The number of finished tests. -* `"nb_running"`: an *unsigned integer*. The number of running tests. +* `"nb_finished"`: a *non-negative integer*. The number of finished tests. +* `"nb_running"`: a *non-negative integer*. The number of running tests. * `"finished_test_ids"`: a list of *test ids*. The set of finished *tests* in this *batch*. @@ -1110,7 +1417,7 @@ An object with the following properties: ## API method: `get_test_params` -Return all *params* objects of a *test*. +Return a normalized *params* objects of a *test*. Example request: @@ -1133,7 +1440,6 @@ Example response: "domain": "zonemaster.net", "profile": "default", "client_id": "Zonemaster Dancer Frontend", - "advanced": true, "nameservers": [ { "ns": "ns3.nic.se", @@ -1171,8 +1477,26 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t > TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. > -[Available profiles]: Configuration.md#profiles-section +[Add_batch_job]: #api-method-add_batch_job +[DS info]: #ds-info +[Delegation Signer]: https://datatracker.ietf.org/doc/html/rfc4034#section-5 +[Dot-decimal notation]: https://en.wikipedia.org/wiki/Dot-decimal_notation [ISO 3166-1 alpha-2]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 [ISO 639-1]: https://en.wikipedia.org/wiki/ISO_639-1 -[Privilege levels]: #privilege-levels +[JSON Pointer]: https://datatracker.ietf.org/doc/html/rfc6901 +[JSON-RPC 2.0]: https://www.jsonrpc.org/specification +[LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag +[Name server]: #name-server +[Privilege levels]: #privilege-levels +[Profile name]: #profile-name +[Profile sections]: Configuration.md#public-profiles-and-private-profiles-sections +[RFC 5952]: https://datatracker.ietf.org/doc/html/rfc5952 +[Severity Level Definitions]: https://github.com/zonemaster/zonemaster/blob/master/docs/specifications/tests/SeverityLevelDefinitions.md +[Start_domain_test]: #api-method-start_domain_test +[Validation error data]: #validation-error-data +[`RPCAPI.enable_add_api_user`]: Configuration.md#enable_add_api_user +[`RPCAPI.enable_add_batch_job`]: Configuration.md#enable_add_batch_job +[`age_reuse_previous_test`]: Configuration.md#age_reuse_previous_test +[net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 +[net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 diff --git a/docs/Configuration.md b/docs/Configuration.md index 60fee297c..1c9ab142d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,15 +1,61 @@ # Configuration +## Table of contents + +* [Introduction](#Introduction) +* [RPCAPI section](#rpcapi-section) + * [enable_add_batch_job](#enable_add_batch_job) + * [enable_add_api_user](#enable_add_api_user) +* [DB section](#DB-section) + * [engine](#engine) + * [user](#user) + * [password](#password) + * [database_host](#database_host) + * [database_name](#database_name) + * [polling_interval](#polling_interval) +* [MYSQL section](#MYSQL-section) + * [host](#host) + * [port](#port) + * [user](#user-1) + * [password](#password-1) + * [database](#database) +* [POSTGRESQL section](#POSTGRESQL-section) + * [host](#host-1) + * [port](#port-1) + * [user](#user-2) + * [password](#password-2) + * [database](#database-1) +* [SQLITE section](#SQLITE-section) + * [database_file](#database_file) +* [LANGUAGE section](#LANGUAGE-section) + * [locale](#locale) +* [METRICS section](#METRICS-section) + * [statsd_host](#statsd_host) + * [statsd_port](#statsd_port) +* [PUBLIC PROFILES and PRIVATE PROFILES sections](#PUBLIC-PROFILES-and-PRIVATE-PROFILES-sections) +* [ZONEMASTER section](#ZONEMASTER-section) + * [max_zonemaster_execution_time](#max_zonemaster_execution_time) + * [number_of_processes_for_frontend_testing](#number_of_processes_for_frontend_testing) + * [number_of_processes_for_batch_testing](#number_of_processes_for_batch_testing) + * [lock_on_queue](#lock_on_queue) + * [age_reuse_previous_test](#age_reuse_previous_test) + +## Introduction + Zonemaster *Backend* is configured in -`/etc/zonemaster/backend_config.ini` (CentOS, Debian and Ubuntu) or +`/etc/zonemaster/backend_config.ini` (Linux) or `/usr/local/etc/zonemaster/backend_config.ini` (FreeBSD). Following [Installation instructions] will create the file with factory settings. -Each section in `backend_config.ini` is documented below. - Restart the `zm-rpcapi` and `zm-testagent` daemons to load the changes made to the `backend_config.ini` file. +The `backend_config.ini` file uses a file format in the INI family that is +described in detail [here][File format]. +Repeating a key name in one section is forbidden. + +Each section in `backend_config.ini` is documented below. + ## RPCAPI section Available keys: `enable_add_batch_job`, `enable_add_api_user`. @@ -77,34 +123,58 @@ over this. ### polling_interval +A strictly positive decimal number. Max 5 and 3 digits in the integer and fraction +components respectively. + Time in seconds between database lookups by Test Agent. Default value: `0.5`. ## MYSQL section -Available keys : `host`, `user`, `password`, `database`. +Available keys : `host`, `port`, `user`, `password`, `database`. ### host +An [LDH domain name] or IP address. + The host name of the machine on which the MySQL server is running. If this property is unspecified, the value of [DB.database_host] is used instead. +### port + +The port the MySQL server is listening on. +Default value: `3306`. + +If [MYSQL.host] is set to `localhost` (but neither `127.0.0.1` nor `::1`), +then the value of the [MYSQL.port] property is discarded as the driver +connects using a UNIX socket (see the [DBD::mysql documentation]). + ### user +An ASCII-only [MariaDB unquoted identifier]. +Max length [80 characters][MariaDB identifier max lengths]. + The name of the user with sufficient permission to access the database. If this property is unspecified, the value of [DB.user] is used instead. ### password +A string of [US ASCII printable characters]. +The first character must be neither space nor `<`. +Max length 100 characters. + The password of the configured user. If this property is unspecified, the value of [DB.password] is used instead. ### database +A US ASCII-only [MariaDB unquoted identifier]. +Max length [64 characters][MariaDB identifier max lengths]. + The name of the database to use. If this property is unspecified, the value of [DB.database_name] is used instead. @@ -112,28 +182,43 @@ If this property is unspecified, the value of [DB.database_name] is used instead ## POSTGRESQL section -Available keys : `host`, `user`, `password`, `database`. +Available keys : `host`, `port`, `user`, `password`, `database`. ### host +An [LDH domain name] or IP address. + The host name of the machine on which the PostgreSQL server is running. If this property is unspecified, the value of [DB.database_host] is used instead. +### port + +The port the PostgreSQL server is listening on. +Default value: `5432`. + ### user +A US ASCII-only [PostgreSQL identifier]. Max length 63 characters. + The name of the user with sufficient permission to access the database. If this property is unspecified, the value of [DB.user] is used instead. ### password +A string of [US ASCII printable characters]. +The first character must be neither space nor `<`. +Max length 100 characters. + The password of the configured user. If this property is unspecified, the value of [DB.password] is used instead. ### database +A US ASCII-only [PostgreSQL identifier]. Max length 63 characters. + The name of the database to use. If this property is unspecified, the value of [DB.database_name] is used instead. @@ -145,6 +230,8 @@ Available keys : `database_file`. ### database_file +An absolute path. + The full path to the SQLite main database file. If this property is unspecified, the value of [DB.database_name] is used instead. @@ -154,26 +241,28 @@ If this property is unspecified, the value of [DB.database_name] is used instead The LANGUAGE section has one key, `locale`. -The value of the `locale` key is a space separated list of -`locale tags` where each tag must match the regular expression -`/^[a-z]{2}_[A-Z]{2}$/`. +### locale + +A string matching one of the following descriptions: +* A space separated list of one or more `locale tags` where each tag matches the + regular expression `/^[a-z]{2}_[A-Z]{2}$/`. +* The empty string. **Deprecated**, remove the LANGUAGE.locale entry or specify + LANGUAGE.locale = en_US instead. + +It is an error to repeat the same `locale tag`. If the `locale` key is empty or absent, the `locale tag` value "en_US" is set by default. +#### Design + The two first characters of a `locale tag` are intended to be an [ISO 639-1] two-character language code and the two last characters are intended to be an [ISO 3166-1 alpha-2] two-character country code. A `locale tag` is a locale setting for the available translation of messages without ".UTF-8", which is implied. -If a new `locale tag` is added to the configuration then the equivalent -MO file should be added to Zonemaster-Engine at the correct place so -that gettext can retrieve it, or else the added `locale tag` will not -add any actual language support. See the -[Zonemaster-Engine share directory] for the existing PO files that are -converted to MO files. (Here we should have a link -to documentation instead.) +#### Usage Removing a language from the configuration file just blocks that language from being allowed. If there are more than one `locale tag` @@ -181,48 +270,22 @@ language from being allowed. If there are more than one `locale tag` all those must be removed to block that language. English is the Zonemaster default language, but it can be blocked -from being allowed by RPC-API by not including it in the -configuration. +from being allowed by RPC-API by including some `locale tag` in the +configuration, but none starting with language code for English ("en"). -In the RPCAPI, `language tag` is used ([Language tag]). The -`language tags` are generated from the `locale tags`. Each -`locale tag` will generate two `language tags`, a short tag -equal to the first two letters (usually the same as a language -code) and a long tag which is equal to the full `locale tag`. -If "en_US" is the `locale tag` then "en" and "en_US" are the -`language tags`. - -If there are two `locale tags` that would give the same short -`language tag` then that is excluded. E.g. "en_US en_UK" will -only give "en_US" and "en_UK" as `language tags`. +#### Out-of-the-box support The default installation and configuration supports the following languages. -Language | Locale tag value | Locale value used ----------|--------------------|------------------ -Danish | da_DK | da_DK.UTF-8 -English | en_US | en_US.UTF-8 -Finnish | fi_FI | fi_FI.UTF-8 -French | fr_FR | fr_FR.UTF-8 -Norwegian| nb_NO | nb_NO.UTF-8 -Swedish | sv_SE | sv_SE.UTF-8 - -The following `language tags` are generated: -* da -* da_DK -* en -* en_US -* fi -* fi_FI -* fr -* fr_FR -* nb -* nb_NO -* sv -* sv_SE - -It is an error to repeat the same `locale tag`. +Language | Locale tag value | Language code | Locale value used +---------|------------------|---------------|------------------ +Danish | da_DK | da | da_DK.UTF-8 +English | en_US | en | en_US.UTF-8 +Finnish | fi_FI | fi | fi_FI.UTF-8 +French | fr_FR | fr | fr_FR.UTF-8 +Norwegian| nb_NO | nb | nb_NO.UTF-8 +Swedish | sv_SE | sv | sv_SE.UTF-8 Setting in the default configuration file: @@ -230,18 +293,54 @@ Setting in the default configuration file: locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE ``` +#### Installation considerations + +If a new `locale tag` is added to the configuration then the equivalent +MO file should be added to Zonemaster-Engine at the correct place so +that gettext can retrieve it, or else the added `locale tag` will not +add any actual language support. The MO file should be created for the +`language code` of the `locale tag` (see the table above), not the entire +`locale tag`. E.g. if the `locale` configuration key includes "sv_SE" then +a MO file for "sv" should be included in the installation. + +Use of MO files based on the entire `locale tag` is *deprecated*. + +See the [Zonemaster-Engine share directory] for the existing PO files that are +converted to MO files during installation. (Here we should have a link +to documentation instead.) + Each locale set in the configuration file, including the implied ".UTF-8", must also be installed or activate on the system running the RPCAPI daemon for the translation to work correctly. +## METRICS section + +### statsd_host + +An [LDH domain name] or IP address. + +The host name of the machine on which the StatsD receiver is running. + +Leave unspecified to disable the metrics. + +Note that this feature requires the `Net::Statsd` module to be installed. + +### statsd_port + +The port the StatsD receiver is listening on. +Default value: `8125`. + ## PUBLIC PROFILES and PRIVATE PROFILES sections The PUBLIC PROFILES and PRIVATE PROFILES sections together define the available [profiles]. -Keys in both sections are [profile names], and values are absolute file system paths to -[profile JSON files]. Keys must not be duplicated between the sections, and the -key `default` must not be present in the PRIVATE PROFILES sections. +Keys in both sections are `profile names`, and values are absolute file system +paths to [profile JSON files]. The key must conform to the character limitation +specified for `profile name` as specified in the API document +[Profile name section]. Keys that only differ in case are considered to be equal. +Keys must not be duplicated between or within the sections, and the key +`default` must not be present in the PRIVATE PROFILES section. There is a `default` profile that is special. It is always available even if not specified. If it is not explicitly mapped to a profile JSON file, it is implicitly @@ -267,28 +366,19 @@ The ZONEMASTER section has several keys : * number_of_processes_for_frontend_testing * number_of_processes_for_batch_testing * lock_on_queue -* maximal_number_of_retries * age_reuse_previous_test ### max_zonemaster_execution_time -An integer. +A strictly positive integer. Max length 5 digits. + Time in seconds before reporting an unfinished test as failed. Default value: `600`. -### maximal_number_of_retries - -An integer. -Number of time a test is allowed to be run again if unfinished after -`max_zonemaster_execution_time`. -Default value: `0`. - -This option is experimental and all edge cases are not fully tested. -Do not use it (keep the default value "0"), or use it with care. - ### number_of_processes_for_frontend_testing -A positive integer. +A strictly positive integer. Max length 5 digits. + Number of processes allowed to run in parallel (added with `number_of_processes_for_batch_testing`). Default value: `20`. @@ -299,7 +389,8 @@ frontend, but is used in combination of ### number_of_processes_for_batch_testing -An integer. +A non-negative integer. Max length 5 digits. + Number of processes allowed to run in parallel (added with `number_of_processes_for_frontend_testing`). Default value: `20`. @@ -310,13 +401,15 @@ batch pool of tests, but is used in combination of ### lock_on_queue -An integer. +A non-negative integer. Max length 5 digits. + A label to associate a test to a specific Test Agent. Default value: `0`. ### age_reuse_previous_test -A positive integer. +A strictly positive integer. Max length 5 digits. + The shelf life of a test in seconds after its creation. Default value: `600`. @@ -325,27 +418,34 @@ shelf life of a previous test result, that test result is reused. Otherwise a new test request is enqueued. - +[API documentation]: API.md [DB.database_host]: #database_host [DB.database_name]: #database_name [DB.password]: #password [DB.user]: #user +[DBD::mysql documentation]: https://metacpan.org/pod/DBD::mysql#host [Default JSON profile file]: https://github.com/zonemaster/zonemaster-engine/blob/master/share/profile.json +[File format]: https://metacpan.org/pod/Config::IniFiles#FILE-FORMAT [ISO 3166-1 alpha-2]: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 [ISO 639-1]: https://en.wikipedia.org/wiki/ISO_639-1 [Installation instructions]: Installation.md [Language tag]: API.md#language-tag +[LDH domain name]: https://datatracker.ietf.org/doc/html/rfc3696#section-2 +[MariaDB identifier max lengths]: https://mariadb.com/kb/en/identifier-names/#maximum-length +[MariaDB unquoted identifier]: https://mariadb.com/kb/en/identifier-names/#unquoted [MYSQL.database]: #database [MYSQL.host]: #host [MYSQL.password]: #password-1 [MYSQL.user]: #user-1 +[PostgreSQL identifier]: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS [POSTGRESQL.database]: #database-1 [POSTGRESQL.host]: #host-1 [POSTGRESQL.password]: #password-2 [POSTGRESQL.user]: #user-2 [Profile JSON files]: https://github.com/zonemaster/zonemaster-engine/blob/master/docs/Profiles.md -[Profile names]: API.md#profile-name +[Profile name section]: API.md#profile-name [Profiles]: Architecture.md#profile [SQLITE.database_file]: #database_file +[US ASCII printable characters]: https://en.wikipedia.org/wiki/ASCII#Printable_characters [Zonemaster-Engine share directory]: https://github.com/zonemaster/zonemaster-engine/tree/master/share [Zonemaster::Engine::Profile]: https://metacpan.org/pod/Zonemaster::Engine::Profile#PROFILE-PROPERTIES diff --git a/docs/Installation.md b/docs/Installation.md index 12d677ab7..3b17219e5 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -1,36 +1,55 @@ # Installation -**Table of contents** +## Table of contents * [1. Overview](#1-overview) * [2. Prerequisites](#2-prerequisites) -* [3. Installation on CentOS](#3-installation-on-centos) - * [3.1 Install Zonemaster::Backend and related dependencies (CentOS)](#31-install-zonemasterbackend-and-related-dependencies-centos) - * [3.2 Database engine installation and configuration (CentOS)](#32-database-engine-installation-and-configuration-centos) - * [3.3 Service configuration and startup (CentOS)](#33-service-configuration-and-startup-centos) - * [3.4 Post-installation (CentOS)](#34-post-installation-centos) -* [4. Installation on Debian](#4-installation-on-debian) - * [4.1 Install Zonemaster::Backend and related dependencies (Debian)](#41-install-zonemasterbackend-and-related-dependencies-debian) - * [4.2 Database engine installation and configuration (Debian)](#42-database-engine-installation-and-configuration-debian) - * [4.3 Service configuration and startup (Debian)](#43-service-configuration-and-startup-debian) - * [4.4 Post-installation (Debian)](#44-post-installation-debian) +* [3. Installation on Rocky Linux](#3-installation-on-rocky-linux) + * [3.1 Install Zonemaster::Backend and related dependencies (Rocky Linux)](#31-install-zonemasterbackend-and-related-dependencies-rocky-linux) + * [3.2 Database engine installation (Rocky Linux)](#32-database-engine-installation-rocky-linux) + * [3.3 Database configuration (Rocky Linux)](#33-database-configuration-rocky-linux) + * [3.4 Service configuration and startup (Rocky Linux)](#34-service-configuration-and-startup-rocky-linux) + * [3.5 Post-installation (Rocky Linux)](#35-post-installation-rocky-linux) +* [4. Installation on Debian and Ubuntu](#4-installation-on-debian-and-ubuntu) + * [4.1 Install Zonemaster::Backend and related dependencies (Debian/Ubuntu)](#41-install-zonemasterbackend-and-related-dependencies-debianubuntu) + * [4.2 Database engine installation (Debian/Ubuntu)](#42-database-engine-installation-debianubuntu) + * [4.3 Database configuration (Debian/Ubuntu)](#43-database-configuration-debianubuntu) + * [4.4 Service configuration and startup (Debian/Ubuntu)](#44-service-configuration-and-startup-debianubuntu) + * [4.5 Post-installation (Debian/Ubuntu)](#45-post-installation-debianubuntu) * [5. Installation on FreeBSD](#5-installation-on-freebsd) * [5.1 Install Zonemaster::Backend and related dependencies (FreeBSD)](#51-install-zonemasterbackend-and-related-dependencies-freebsd) - * [5.2 Database engine installation and configuration (FreeBSD)](#52-database-engine-installation-and-configuration-freebsd) - * [5.3 Service startup (FreeBSD)](#53-service-startup-freebsd) - * [5.4 Post-installation (FreeBSD)](#54-post-installation-freebsd) -* [6. Installation on Ubuntu](#6-installation-on-ubuntu) -* [7. Post-installation](#7-post-installation) - * [7.1 Smoke test](#71-smoke-test) - * [7.2 What to do next?](#72-what-to-do-next) - * [7.3 Cleaning up the database](#73-cleaning-up-the-database) - * [7.4 Upgrade Zonemaster database](#74-upgrade-zonemaster-database) + * [5.2 Database engine installation (FreeBSD)](#52-database-engine-installation-freebsd) + * [5.3 Database configuration (FreeBSD)](#53-database-configuration-freebsd) + * [5.4 Service startup (FreeBSD)](#54-service-startup-freebsd) + * [5.5 Post-installation (FreeBSD)](#55-post-installation-freebsd) +* [6. Post-installation](#6-post-installation) + * [6.1 Smoke test](#61-smoke-test) + * [6.2 What to do next?](#62-what-to-do-next) +* [7. Installation with MariaDB](#7-installation-with-mariadb) + * [7.1 MariaDB (Rocky Linux)](#71-mariadb-rocky-linux) + * [7.2. MariaDB (Debian/Ubuntu)](#72-mariadb-debianubuntu) + * [7.3. MySQL (FreeBSD)](#73-mysql-freebsd) +* [8. Installation with PostgreSQL](#8-installation-with-postgresql) + * [8.1. PostgreSQL (Rocky Linux)](#81-postgresql-rocky-linux) + * [8.2. PostgreSQL (Debian/Ubuntu)](#82-postgresql-debianubuntu) + * [8.3. PostgreSQL (FreeBSD)](#83-postgresql-freebsd) +* [9. Cleaning up the database](#9-cleaning-up-the-database) + * [9.1. MariaDB and MySQL](#91-mariadb-and-mysql) + * [9.2. PostgreSQL](#92-postgresql) + * [9.3. SQLite](#93-sqlite) +* [10. Optional features](#10-optional-features) + * [10.1. Metrics](#101-metrics) ## 1. Overview This document contains all steps needed to install Zonemaster::Backend. For an overview of the Zonemaster product, please see the [main Zonemaster Repository]. +If you upgrade your Zonemaster installation with a newer version of +Zonemaster::Backend instead, and want to keep the database, then see the +[Upgrade document]. Otherwise [remove the database][Removing database] and +continue with this installation document. + ## 2. Prerequisites @@ -50,9 +69,9 @@ For details on supported versions of Perl, database engine and operating system for Zonemaster::Backend, see the [declaration of prerequisites]. -## 3. Installation on CentOS +## 3. Installation on Rocky Linux -### 3.1 Install Zonemaster::Backend and related dependencies (CentOS) +### 3.1 Install Zonemaster::Backend and related dependencies (Rocky Linux) > **Note:** Zonemaster::LDNS and Zonemaster::Engine are not listed here as they > are dealt with in the [prerequisites](#prerequisites) section. @@ -60,7 +79,7 @@ for Zonemaster::Backend, see the [declaration of prerequisites]. Install dependencies available from binary packages: ```sh -sudo yum -y install jq perl-Class-Method-Modifiers perl-Config-IniFiles perl-DBD-SQLite perl-DBI perl-HTML-Parser perl-JSON-RPC perl-libwww-perl perl-Log-Dispatch perl-Net-Server perl-Parallel-ForkManager perl-Plack perl-Plack-Test perl-Role-Tiny perl-Router-Simple perl-String-ShellQuote perl-Test-NoWarnings perl-Test-Warn perl-Try-Tiny redhat-lsb-core +sudo dnf -y install jq perl-Class-Method-Modifiers perl-Config-IniFiles perl-DBD-SQLite perl-DBI perl-HTML-Parser perl-JSON-RPC perl-libwww-perl perl-Log-Dispatch perl-Mojolicious perl-Net-Server perl-Parallel-ForkManager perl-Plack perl-Plack-Test perl-Role-Tiny perl-Router-Simple perl-String-ShellQuote perl-Test-NoWarnings perl-Test-Warn perl-Try-Tiny perl-libintl redhat-lsb-core ``` > **Note:** perl-Net-Server and perl-Test-Warn are listed here even though they @@ -100,125 +119,45 @@ sudo install -v -m 755 ./zm-testagent.lsb /etc/init.d/zm-testagent sudo install -v -m 755 ./tmpfiles.conf /usr/lib/tmpfiles.d/zonemaster.conf ``` -> If this is an update of Zonemaster-Backend, you should remove any -> `/etc/init.d/zm-backend.sh` and `/etc/init.d/zm-centos.sh` (scripts from -> previous version of Zonemaster-Backend). - - -### 3.2 Database engine installation and configuration (CentOS) +### 3.2 Database engine installation (Rocky Linux) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. The installation instructions below assumes that this is a new installation. -If you upgrade and want to keep the database, go to section -[7.4](#74-upgrade-zonemaster-database) first. If you instead want to start -from afresh, then go to section [7.3](#73-cleaning-up-the-database) and remove -the old database first. - -If you keep the database, skip the initialization of the Zonemaster database, -but if you have removed the old Zonemaster database, then do the initialization. - - -#### 3.2.1 Instructions for MariaDB (CentOS) - -Configure Zonemaster::Backend to use the correct database engine: - -```sh -sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini -``` - -> **Note:** See the [backend configuration] documentation for details. - -Install, configure and start database engine: - -```sh -sudo yum -y install mariadb-server -sudo systemctl enable mariadb -sudo systemctl start mariadb -``` - -Initialize the database (unless you keep an old database): - -```sh -sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-mysql.sql -``` - -> **Note:** This creates a database called `zonemaster`, as well as a user -> called "zonemaster" with the password "zonemaster" (as stated in the config -> file). This user has just enough permissions to run the backend software. - - -#### 3.2.2 Instructions for PostgreSQL (CentOS) - -Configure Zonemaster::Backend to use the correct database engine: - -```sh -sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini -``` - -> **Note:** See the [backend configuration] documentation for details. - -Install, configure and start database engine: - -* On CentOS 7: - ```sh - sudo rpm -iUvh https://yum.postgresql.org/9.3/redhat/rhel-7-x86_64/pgdg-centos93-9.3-3.noarch.rpm - sudo yum -y install postgresql93-server perl-DBD-Pg - sudo /usr/pgsql-9.3/bin/postgresql93-setup initdb - sudo sed -i '/^[^#]/ s/ident$/md5/' /var/lib/pgsql/9.3/data/pg_hba.conf - sudo systemctl enable postgresql-9.3 - sudo systemctl start postgresql-9.3 - ``` +#### 3.2.1 Instructions for SQLite (Rocky Linux) -* On CentOS 8: - - ```sh - sudo yum -y install postgresql-server perl-DBD-Pg - sudo postgresql-setup --initdb --unit postgresql - sudo sed -i '/^[^#]/ s/ident$/md5/' /var/lib/pgsql/data/pg_hba.conf - sudo systemctl enable postgresql - sudo systemctl start postgresql - ``` +> **Note:** Zonemaster with SQLite is not meant for an installation with heavy +> load. -Initialize Zonemaster database (unless you keep an old database): +Create database directory: ```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql +sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster ``` -> **Note:** This creates a database called `zonemaster`, as well as a user called -> "zonemaster" with the password "zonemaster" (as stated in the config file). -> This user has just enough permissions to run the backend software. +> Some parameters can be changed, see the [backend configuration] documentation +> for details. -#### 3.2.3 Instructions for SQLite (CentOS) +#### 3.2.2 Instructions for other engines (Rocky Linux) -> **Note:** Zonemaster with SQLite is not meant for an installation with heavy -> load. +See sections for [MariaDB][MariaDB instructions Rocky Linux] and +[PostgreSQL][PostgreSQL instructions Rocky Linux]. -Configure Zonemaster::Backend to use the correct database engine and database -path: -```sh -sudo sed -i '/\bengine\b/ s/=.*/= SQLite/' /etc/zonemaster/backend_config.ini -``` +### 3.3 Database configuration (Rocky Linux) -Create database directory, set correct ownership and create database: +Create the database tables: ```sh -cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` -sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster -sudo perl create_db_sqlite.pl -sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite +sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` -> **Note:** See the [backend configuration] documentation for details. - -### 3.3 Service configuration and startup (CentOS) +### 3.4 Service configuration and startup (Rocky Linux) Make sure our tmpfiles configuration takes effect: @@ -236,14 +175,14 @@ sudo systemctl start zm-testagent ``` -### 3.4 Post-installation (CentOS) +### 3.5 Post-installation (Rocky Linux) See the [post-installation] section for post-installation matters. -## 4. Installation on Debian +## 4. Installation on Debian and Ubuntu -### 4.1 Install Zonemaster::Backend and related dependencies (Debian) +### 4.1 Install Zonemaster::Backend and related dependencies (Debian/Ubuntu) > **Note:** Zonemaster::LDNS and Zonemaster::Engine are not listed here as they > are dealt with in the [prerequisites](#prerequisites) section. @@ -268,17 +207,15 @@ sv_SE.utf8 Install dependencies available from binary packages: ```sh -sudo apt install jq libclass-method-modifiers-perl libconfig-inifiles-perl libdbd-sqlite3-perl libdbi-perl libfile-sharedir-perl libfile-slurp-perl libhtml-parser-perl libio-stringy-perl libjson-pp-perl libjson-rpc-perl liblog-any-adapter-dispatch-perl liblog-any-perl liblog-dispatch-perl libmoose-perl libparallel-forkmanager-perl libplack-perl libplack-middleware-debug-perl libplack-middleware-reverseproxy-perl librole-tiny-perl librouter-simple-perl libstring-shellquote-perl libtest-nowarnings-perl libtry-tiny-perl starman +sudo apt install jq libclass-method-modifiers-perl libconfig-inifiles-perl libdbd-sqlite3-perl libdaemon-control-perl libdbi-perl libfile-sharedir-perl libfile-slurp-perl libhtml-parser-perl libmojolicious-perl libio-stringy-perl libjson-pp-perl libjson-rpc-perl libjson-validator-perl liblog-any-adapter-dispatch-perl liblog-any-perl liblog-dispatch-perl libmoose-perl libparallel-forkmanager-perl libplack-perl libplack-middleware-debug-perl libplack-middleware-reverseproxy-perl librole-tiny-perl librouter-simple-perl libstring-shellquote-perl libtest-nowarnings-perl libtry-tiny-perl libintl-perl perl-doc starman ``` - > **Note**: libio-stringy-perl is listed here even though it's not a direct > dependency. It's an undeclared dependency of libconfig-inifiles-perl. -Install dependencies not available from binary packages: - -```sh -sudo cpanm Daemon::Control JSON::Validator -``` +* On Ubuntu, install the following dependency from CPAN: + ``` + sudo cpanm JSON::Validator + ``` Install Zonemaster::Backend: @@ -311,104 +248,45 @@ sudo install -v -m 755 ./tmpfiles.conf /usr/lib/tmpfiles.d/zonemaster.conf > `/etc/init.d/zm-backend.sh` (script from previous version of Zonemaster-Backend). -### 4.2 Database engine installation and configuration (Debian) +### 4.2 Database engine installation (Debian/Ubuntu) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. The installation instructions below assumes that this is a new installation. -If you upgrade and want to keep the database, go to section -[7.4](#74-upgrade-zonemaster-database) first. If you instead want to start -from afresh, then go to section [7.3](#73-cleaning-up-the-database) and remove -the old database first. - -If you keep the database, skip the initialization of the Zonemaster database, -but if you have removed the old Zonemaster database, then do the initialization. - -#### 4.2.1 Instructions for MariaDB (Debian) - -Install the database engine and its dependencies: - -```sh -sudo apt install mariadb-server libdbd-mysql-perl -``` - -Configure Zonemaster::Backend to use the correct database engine: - -```sh -sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini -``` - -> **Note:** See the [backend configuration] documentation for details. -Initialize Zonemaster database (unless you keep an old database): +#### 4.2.1 Instructions for SQLite (Debian/Ubuntu) -```sh -sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-mysql.sql -``` - -> **Note:** This creates a database called `zonemaster`, as well as a user -> called "zonemaster" with the password "zonemaster" (as stated in the config -> file). This user has just enough permissions to run the backend software. - - -#### 4.2.2 Instructions for PostgreSQL (Debian) - -Install database engine and Perl bindings: - -```sh -sudo apt install postgresql libdbd-pg-perl -``` - -Configure Zonemaster::Backend to use the correct database engine: - -```sh -sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini -``` - -> **Note:** See the [backend configuration] documentation for details. +> **Note:** Zonemaster with SQLite is not meant for an installation with heavy +> load. -Initialize Zonemaster database (unless you keep an old database): +Create database directory: ```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql +sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster ``` -> **Note:** This creates a database called `zonemaster`, as well as a user called -> "zonemaster" with the password "zonemaster" (as stated in the config file). -> This user has just enough permissions to run the backend software. +> Some parameters can be changed, see the [backend configuration] documentation +> for details. -#### 4.2.3 Instructions for SQLite (Debian) +#### 4.2.2 Instructions for other engines (Debian/Ubuntu) -> **Note:** Zonemaster with SQLite is not meant for an installation with heavy -> load. +See sections for [MariaDB][MariaDB instructions Debian] and +[PostgreSQL][PostgreSQL instructions Debian]. -> All binaries and Perl bindings are already installed. -Configure Zonemaster::Backend to use the correct database engine and database -path: - -```sh -sudo sed -i '/\bengine\b/ s/=.*/= SQLite/' /etc/zonemaster/backend_config.ini -``` +### 4.3 Database configuration (Debian/Ubuntu) -Create database directory, set correct ownership and create database: +Create the database tables: ```sh -cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` -sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster -sudo perl create_db_sqlite.pl -sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite +sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` -> SQLite will not run as a daemon and does not need to be started. - -> **Note:** See the [backend configuration] documentation for details. - -### 4.3 Service configuration and startup (Debian) +### 4.4 Service configuration and startup (Debian/Ubuntu) Make sure our tmpfiles configuration takes effect: @@ -426,7 +304,7 @@ sudo systemctl start zm-testagent ``` -### 4.4 Post-installation (Debian) +### 4.5 Post-installation (Debian/Ubuntu) See the [post-installation] section for post-installation matters. @@ -447,11 +325,10 @@ su -l Install dependencies available from binary packages: ```sh -pkg install jq p5-Class-Method-Modifiers p5-Config-IniFiles p5-Daemon-Control p5-DBI p5-File-ShareDir p5-File-Slurp p5-HTML-Parser p5-JSON-PP p5-JSON-RPC p5-Moose p5-Parallel-ForkManager p5-Plack p5-Plack-Middleware-ReverseProxy p5-Role-Tiny p5-Router-Simple p5-Starman p5-String-ShellQuote p5-DBD-SQLite p5-Log-Dispatch p5-Log-Any p5-Log-Any-Adapter-Dispatch p5-JSON-Validator p5-YAML-LibYAML p5-Test-NoWarnings +pkg install jq p5-Class-Method-Modifiers p5-Config-IniFiles p5-Daemon-Control p5-DBI p5-File-ShareDir p5-File-Slurp p5-HTML-Parser p5-JSON-PP p5-JSON-RPC p5-Mojolicious p5-Moose p5-Parallel-ForkManager p5-Plack p5-Plack-Middleware-ReverseProxy p5-Role-Tiny p5-Router-Simple p5-Starman p5-String-ShellQuote p5-DBD-SQLite p5-Log-Dispatch p5-Log-Any p5-Log-Any-Adapter-Dispatch p5-JSON-Validator p5-YAML-LibYAML p5-Test-NoWarnings p5-Locale-libintl gmake ``` - Install Zonemaster::Backend: ```sh @@ -481,186 +358,342 @@ install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi install -v -m 755 ./zm_testagent-bsd /usr/local/etc/rc.d/zm_testagent ``` -### 5.2 Database engine installation and configuration (FreeBSD) +### 5.2 Database engine installation (FreeBSD) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. The installation instructions below assumes that this is a new installation. -If you upgrade and want to keep the database, go to section -[7.4](#74-upgrade-zonemaster-database) first. If you instead want to start -from afresh, then go to section [7.3](#73-cleaning-up-the-database) and remove -the old database first. -If you keep the database, skip the initialization of the Zonemaster database, -but if you have removed the old Zonemaster database, then do the initialization. +#### 5.2.1 Instructions for SQLite (FreeBSD) -#### 5.2.1 Instructions for MySQL (FreeBSD) +> **Note:** Zonemaster with SQLite is not meant for an installation with heavy +> load. -Configure Zonemaster::Backend to use the correct database engine: +Configure Zonemaster::Backend to use the correct database path: ```sh -sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= MySQL/' /usr/local/etc/zonemaster/backend_config.ini +sed -i '' '/[[:<:]]database_file[[:>:]]/ s:=.*:= /var/db/zonemaster/db.sqlite:' /usr/local/etc/zonemaster/backend_config.ini ``` -> **Note:** See the [backend configuration] documentation for details. -Install, configure and start database engine (and Perl bindings): +Create database directory: ```sh -pkg install mysql57-server p5-DBD-mysql -sysrc mysql_enable="YES" -service mysql-server start +install -v -m 755 -o zonemaster -g zonemaster -d /var/db/zonemaster ``` -Read the current root password for MySQL: +> Some parameters can be changed, see the [backend configuration] documentation +> for details. + +#### 5.2.2 Instructions for other engines (FreeBSD) + +See sections for [MariaDB][MariaDB instructions FreeBSD] and +[PostgreSQL][PostgreSQL instructions FreeBSD]. + + +### 5.3 Database configuration (FreeBSD) + +Create the database tables: ```sh -cat /root/.mysql_secret +su -m zonemaster -c "`perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir(qw(Zonemaster-Backend))'`/create_db.pl" ``` -Connect to MySQL interactively: + +### 5.4 Service startup (FreeBSD) + +Enable services at startup and start service: ```sh -mysql -u root -h localhost -p +sysrc zm_rpcapi_enable="YES" +sysrc zm_testagent_enable="YES" +service zm_rpcapi start +service zm_testagent start ``` -Reset root password in MySQL (required by MySQL). Replace -`` with the password from the file above -(or another one of your choice): +### 5.5 Post-installation (FreeBSD) -```sql -ALTER USER 'root'@'localhost' IDENTIFIED BY ''; +To check that the running daemons run: + +```sh +service zm_rpcapi status +service zm_testagent status ``` -Logout from database: +See the [post-installation] section for post-installation matters. + + +## 6. Post-installation + +### 6.1 Smoke test + +If you have followed the installation instructions for Zonemaster::Backend above, +you should be able to use the API on localhost port 5000 as below. -```sql -exit; +```sh +zmtest zonemaster.net ``` -Unless you keep an old database, initialize the database (and give the -root password when prompted): +The command is expected to immediately print out a testid, +followed by a percentage ticking up from 0% to 100%. +Once the number reaches 100% a JSON object is printed and zmtest terminates. + + +### 6.2. What to do next? + +* For a web interface, follow the [Zonemaster::GUI installation] instructions. +* For a command line interface, follow the [Zonemaster::CLI installation] instruction. +* For a JSON-RPC API, see the Zonemaster::Backend [JSON-RPC API] documentation. + + +## 7. Installation with MariaDB + +First follow the installation instructions for the OS in question, and then go +to this section to install MariaDB. + +### 7.1. MariaDB (Rocky Linux) + +Configure Zonemaster::Backend to use the correct database engine: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -mysql -u root -p < ./initial-mysql.sql +sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini ``` -> **Note:** This creates a database called `zonemaster`, as well as a user -> called "zonemaster" with the password "zonemaster" (as stated in the config -> file). This user has just enough permissions to run the backend software. +> **Note:** See the [backend configuration] documentation for details. + +Install, configure and start database engine: + +```sh +sudo dnf -y install mariadb-server +sudo systemctl enable mariadb +sudo systemctl start mariadb +``` -#### 5.2.2 Instructions for PostgreSQL (FreeBSD) +To create the database and the database user (unless you keep an old database). +Edit the commands first if you want a non-default database name, user name or +password. To be safe, run the commands one by one. + +```sh +sudo mysql -e "CREATE DATABASE zonemaster;" +``` +```sh +sudo mysql -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" +``` +```sh +sudo mysql -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" +``` + +Update the `/etc/zonemaster/backend_config.ini` file with database name, +username and password if non-default values are used. + +Now go back to "[Database configuration](#33-database-configuration-rocky-linux)" +to create the database tables and then continue with the steps after that. + + +### 7.2. MariaDB (Debian/Ubuntu) Configure Zonemaster::Backend to use the correct database engine: ```sh -sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= PostgreSQL/' /usr/local/etc/zonemaster/backend_config.ini +sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini ``` + > **Note:** See the [backend configuration] documentation for details. -Install, configure and start database engine (and Perl bindings): +Install the database engine and its dependencies: ```sh -pkg install postgresql12-server p5-DBD-Pg -sysrc postgresql_enable="YES" -service postgresql initdb -service postgresql start +sudo apt install mariadb-server libdbd-mysql-perl ``` -Initialize Zonemaster database (unless you keep an old database): +To create the database and the database user (unless you keep an old database). +Edit the commands first if you want a non-default database name, user name or +password. To be safe, run the commands one by one. ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -psql -U postgres -f ./initial-postgres.sql +sudo mysql -e "CREATE DATABASE zonemaster;" +``` +```sh +sudo mysql -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" +``` +```sh +sudo mysql -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" ``` -#### 5.2.3 Instructions for SQLite (FreeBSD) +Update the `/etc/zonemaster/backend_config.ini` file with database name, username +and password if non-default values are used. -> **Note:** Zonemaster with SQLite is not meant for an installation with heavy -> load. +Now go back to "[Database configuration](#43-database-configuration-debianubuntu)" +to create the database tables and then continue with the steps after that. + + +### 7.3. MySQL (FreeBSD) -> All binaries and Perl bindings are already installed. +> MariaDB is not compatible with Zonemaster on FreeBSD. MySQL is used instead. -Configure Zonemaster::Backend to use the correct database engine and database -path: +Configure Zonemaster::Backend to use the correct database engine: ```sh -sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= SQLite/' /usr/local/etc/zonemaster/backend_config.ini -sed -i '' '/[[:<:]]database_file[[:>:]]/ s:=.*:= /var/db/zonemaster/db.sqlite:' /usr/local/etc/zonemaster/backend_config.ini +sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= MySQL/' /usr/local/etc/zonemaster/backend_config.ini ``` +> **Note:** See the [backend configuration] documentation for details. -Create database directory, set correct ownership and create database: +Install, configure and start database engine (and Perl bindings): ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -install -v -m 755 -o zonemaster -g zonemaster -d /var/db/zonemaster -env ZONEMASTER_BACKEND_CONFIG_FILE=/usr/local/etc/zonemaster/backend_config.ini su -m zonemaster -c "perl create_db_sqlite.pl" +pkg install -y mysql57-server p5-DBD-mysql +sysrc mysql_enable="YES" +service mysql-server start +``` + +Read the current root password for MySQL (unless it has been changed +already). + +```sh +cat /root/.mysql_secret ``` -> SQLite will not run as a daemon and does not need to be started. +Set password for MySQL root (required by MySQL). Use the password from +`/root/.mysql_secret` when prompted for password, and then the new password +when prompted for that. + +```sh +/usr/local/bin/mysqladmin -u root -p password '' +``` + +To create the database and the database user (unless you keep an old database). +Edit the command first if you want a non-default database name, user name or +password. Run the command on one line. Use the MySQL root password when +prompted. + +```sh +mysql -u root -p -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" +``` + +Update the `/usr/local/etc/zonemaster/backend_config.ini` file with database +name, username and password if non-default values are used. + +Now go back to "[Database configuration](#53-database-configuration-freebsd)" +to create the database tables and then continue with the steps after that. + + +## 8. Installation with PostgreSQL + +First follow the installation instructions for the OS in question, and then go +to this section to install PostgreSQL. + +### 8.1. PostgreSQL (Rocky Linux) + +Configure Zonemaster::Backend to use the correct database engine: + +```sh +sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini +``` > **Note:** See the [backend configuration] documentation for details. -### 5.3 Service startup (FreeBSD) +Install, configure and start database engine: -Enable services at startup: +```sh +sudo dnf -y install postgresql-server perl-DBD-Pg +sudo postgresql-setup --initdb --unit postgresql +sudo sed -i '/^[^#]/ s/ident$/md5/' /var/lib/pgsql/data/pg_hba.conf +sudo systemctl enable postgresql +sudo systemctl start postgresql +``` + +To create the database and the database user (unless you keep an old database). +Edit the command first if you want a non-default database name, user name or +password. To be safe run the commands one by one. ```sh -sysrc zm_rpcapi_enable="YES" -sysrc zm_testagent_enable="YES" +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" +``` +```sh +sudo -u postgres psql -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` -Start services: +> **Note:** You may get error messages from these commands about lack of +> permission to change directory. You can safely ignore those messages. + +Update the `/etc/zonemaster/backend_config.ini` file with database name, username +and password if non-default values are used. + +Now go back to "[Database configuration](#33-database-configuration-rocky-linux)" +to create the database tables and then continue with the steps after that. + + +### 8.2. PostgreSQL (Debian/Ubuntu) + +Configure Zonemaster::Backend to use the correct database engine: ```sh -service zm_rpcapi start -service zm_testagent start +sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini ``` -### 5.4 Post-installation (FreeBSD) +Install the database engine and Perl bindings: -To check the running daemons run: +```sh +sudo apt install postgresql libdbd-pg-perl +``` + +> **Note:** See the [backend configuration] documentation for details. + +To create the database and the database user (unless you keep an old database). +Edit the command first if you want a non-default database name, user name or +password. To be safe run the commands one by one. ```sh -service mysql-server status # If mysql-server is installed -service postgresql status # If postgresql is installed -service zm_rpcapi status -service zm_testagent status +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" + ``` +```sh +sudo -u postgres psql -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" -See the [post-installation] section for post-installation matters. +``` +Update the `/etc/zonemaster/backend_config.ini` file with database name, username +and password if non-default values are used. -## 6. Installation on Ubuntu +Now go back to "[Database configuration](#43-database-configuration-debianubuntu)" +to create the database tables and then continue with the steps after that. -Use the procedure for installation on [Debian](#4-installation-on-debian). +### 8.3. PostgreSQL (FreeBSD) -## 7. Post-installation +Configure Zonemaster::Backend to use the correct database engine: -### 7.1 Smoke test +```sh +sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= PostgreSQL/' /usr/local/etc/zonemaster/backend_config.ini +``` +> **Note:** See the [backend configuration] documentation for details. -If you have followed the installation instructions for Zonemaster::Backend above, -you should be able to use the API on localhost port 5000 as below. +Install, configure and start database engine (and Perl bindings): ```sh -zmtest zonemaster.net +pkg install -y postgresql12-server p5-DBD-Pg +sysrc postgresql_enable="YES" +service postgresql initdb +service postgresql start ``` -The command is expected to immediately print out a testid, -followed by a percentage ticking up from 0% to 100%. -Once the number reaches 100% a JSON object is printed and zmtest terminates. +To create the database and the database user (unless you keep an old database). +Edit the commands first if you want a non-default database name, user name or +password. +```sh +psql -U postgres -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" +psql -U postgres -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" +``` -### 7.2. What to do next? +Update the `/usr/local/etc/zonemaster/backend_config.ini` file with database +name, username and password if non-default values are used. -* For a web interface, follow the [Zonemaster::GUI installation] instructions. -* For a command line interface, follow the [Zonemaster::CLI installation] instruction. -* For a JSON-RPC API, see the Zonemaster::Backend [JSON-RPC API] documentation. +Now go back to "[Database configuration](#53-database-configuration-freebsd)" +to create the database tables and then continue with the steps after that. -### 7.3. Cleaning up the database +## 9. Cleaning up the database If, at some point, you want to delete all traces of Zonemaster in the database, you can run the file `cleanup-mysql.sql` or file `cleanup-postgres.sql` @@ -668,43 +701,87 @@ as a database administrator. Commands for locating and running the file are below. It removes the user and drops the database (obviously taking all data with it). -#### 7.3.1 MySQL +> Each script uses default values, you may need to adapt them to your setup. + +### 9.1. MariaDB and MySQL + +Rocky Linux, Debian and Ubuntu: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -mysql --user=root --password < ./cleanup-mysql.sql +sudo mysql --user=root < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql ``` -#### 7.3.2 PostgreSQL +FreeBSD (you will get prompted for MySQL password): ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! +mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql +``` + +### 9.2. PostgreSQL + +Rocky Linux, Debian and Ubuntu: + +```sh +sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/cleanup-postgres.sql ``` -#### 7.3.3 SQLite +FreeBSD (as root): + +```sh +psql -U postgres -f `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-postgres.sql +``` + +### 9.3. SQLite Remove the database file and recreate it following the installation instructions above. -### 7.4 Upgrade Zonemaster database +## 10. Optional features -If you upgrade your Zonemaster installation with a newer version of -Zonemaster-Backend and keep the database, then you might have to upgrade the -database to use it with the new version of Zonemaster-Backend. Please see the -[upgrade][README.md-upgrade] information. +### 10.1 Metrics -For SQLite database upgrading is not needed as of now. +Statsd metrics are available, to enable the feature install the additional +`Net::Statsd` module. See the [configuration][Backend configuration] to +configure the receiver. + +The list of metrics is available in the [Telemetry document][metrics]. + +### 10.1.1 Installation on Rocky Linux + +```sh +sudo cpanm Net::Statsd +``` + +### 10.1.2 Installation on Debian / Ubuntu + + +```sh +sudo apt install libnet-statsd-perl +``` + +### 10.1.3 Installation on Freebsd + +```sh +cpanm Net::Statsd +``` ------- -[Backend configuration]: Configuration.md -[Declaration of prerequisites]: https://github.com/zonemaster/zonemaster#prerequisites -[JSON-RPC API]: API.md -[Main Zonemaster repository]: https://github.com/zonemaster/zonemaster/blob/master/README.md -[Post-installation]: #7-post-installation -[README.md-upgrade]: /README.md#upgrade -[Zonemaster::CLI installation]: https://github.com/zonemaster/zonemaster-cli/blob/master/docs/Installation.md -[Zonemaster::GUI installation]: https://github.com/zonemaster/zonemaster-gui/blob/master/docs/Installation.md -[Zonemaster::Engine installation]: https://github.com/zonemaster/zonemaster-engine/blob/master/docs/Installation.md -[Zonemaster::Engine]: https://github.com/zonemaster/zonemaster-engine/blob/master/README.md -[Zonemaster::LDNS]: https://github.com/zonemaster/zonemaster-ldns/blob/master/README.md +[Backend configuration]: Configuration.md +[Declaration of prerequisites]: https://github.com/zonemaster/zonemaster#prerequisites +[JSON-RPC API]: API.md +[Main Zonemaster repository]: https://github.com/zonemaster/zonemaster/blob/master/README.md +[MariaDB instructions Rocky Linux]: #71-mariadb-rocky-linux +[MariaDB instructions Debian]: #72-mariadb-debianubuntu +[MariaDB instructions FreeBSD]: #73-mysql-freebsd +[metrics]: Telemetry.md#metrics +[Post-installation]: #6-post-installation +[PostgreSQL instructions Rocky Linux]: #81-postgresql-rocky-linux +[PostgreSQL instructions Debian]: #82-postgresql-debianubuntu +[PostgreSQL instructions FreeBSD]: #83-postgresql-freebsd +[Removing database]: #9-cleaning-up-the-database +[Upgrade document]: Upgrade.md +[Zonemaster::CLI installation]: https://github.com/zonemaster/zonemaster-cli/blob/master/docs/Installation.md +[Zonemaster::Engine installation]: https://github.com/zonemaster/zonemaster-engine/blob/master/docs/Installation.md +[Zonemaster::Engine]: https://github.com/zonemaster/zonemaster-engine/blob/master/README.md +[Zonemaster::GUI installation]: https://github.com/zonemaster/zonemaster-gui/blob/master/docs/Installation.md +[Zonemaster::LDNS]: https://github.com/zonemaster/zonemaster-ldns/blob/master/README.md diff --git a/docs/Telemetry.md b/docs/Telemetry.md new file mode 100644 index 000000000..fdc2c435f --- /dev/null +++ b/docs/Telemetry.md @@ -0,0 +1,73 @@ +# Telemetry + +## Metrics + +If enabled in the [Metrics section][metrics feature] in the configuration file, +[Statsd][statsd] compatible metrics are available to use: + +| Name | Type | Description | +| ------------------------------------------------ | ------- | ----------- | +| zonemaster.rpcapi.requests.\.\ | Counter | Number of times the JSON RPC method \ resulted in JSON RPC status \. The status is represented in string, possible values are: `RPC_PARSE_ERROR`, `RPC_INVALID_REQUEST`, `RPC_METHOD_NOT_FOUND`, `RPC_INVALID_PARAMS`, `RPC_INTERNAL_ERROR`. | +| zonemaster.testagent.tests_started | Counter | Number of tests that have started. | +| zonemaster.testagent.tests_completed | Counter | Number of tests that have been completed successfully. | +| zonemaster.testagent.tests_died | Counter | Number of tests that have died. | +| zonemaster.testagent.tests_duration_seconds | Timing | The duration of a test, emitted for each test. | +| zonemaster.testagent.cleanup_duration_seconds | Timing | Time spent to kill timed out processes. | +| zonemaster.testagent.fetchtests_duration_seconds | Timing | Time spent selecting the next text to run and processing unfinished tests. | +| zonemaster.testagent.running_processes | Gauge | Number of running processes in a test agent. | +| zonemaster.testagent.maximum_processes | Gauge | Maximum number of running processes in a test agent. | + + +### Usage + +Testing the metrics feature can be as easy as running a listening UDP server like + +```sh +ns -lup 8125 +``` + +This should be enough to see the metrics emitted by Zonemaster. + +More complex setups are required for the metrics to be used in alerts and dashboards. +StatsD metrics can be integrated to a number of metrics backend like Prometheus (using the [StatsD exporter]), InfluxDB (using Telegraf and the [StatsD plugin]), Graphite ([integration guide]) and others. + +#### StatsD Exporter (Prometheus) + +1. Download the binary (tar file) corresponding to your environment https://github.com/prometheus/statsd_exporter/releases +2. Untar the file +3. `cd` into the statsd_exporter directory +4. Create a `statsd_mapping.yml` file with content as below + ```yml + mappings: + - match: zonemaster.rpcapi.requests.*.* + name: zonemaster_rpcapi_requests_total + labels: + method: $1 + status: $2 + - match: zonemaster.testagent.tests_duration_seconds + observer_type: histogram + buckets: [ 1, 2.5, 5, 10, 15, 30, 45, 60, 75, 90, 105, 120, 150, 180] + name: zonemaster_testagent_tests_duration_seconds + - match: zonemaster.testagent.cleanup_duration_seconds + observer_type: histogram + buckets: [ 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] + name: zonemaster_testagent_cleanup_duration_seconds + - match: zonemaster.testagent.fetchtests_duration_seconds + observer_type: histogram + buckets: [ 0.001, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 5, 10] + name: zonemaster_testagent_fetchtests_duration_seconds + ``` +5. Run it like + ``` + ./statsd_exporter --statsd.mapping-config=./statsd_mapping.yml --statsd.listen-udp=:8125 --statsd.listen-tcp=:8125 + ``` +6. Run the following to see Zonemaster metrics: + ``` + curl localhost:9102/metrics | grep zonemaster + ``` + +[metrics feature]: Installation.md#101-metrics +[statsd]: https://github.com/statsd/statsd +[StatsD exporter]: https://github.com/prometheus/statsd_exporter +[StatsD plugin]: https://github.com/influxdata/telegraf/tree/master/plugins/inputs/statsd +[integration guide]: https://github.com/statsd/statsd/blob/master/docs/graphite.md diff --git a/docs/Upgrade.md b/docs/Upgrade.md new file mode 100644 index 000000000..6826a53e1 --- /dev/null +++ b/docs/Upgrade.md @@ -0,0 +1,61 @@ +# Upgrade + +## 1. Overview + +This document contains pointer to instructions on how to upgrade the +Zonemaster::Backend component. An upgrade usually consist of an upgrade script +to upgrade the database, and instructions to install new dependencies. + +> When upgrading from a version < v6.2.0 to a version ≥ v8.0.0, it is +> recommended to install the desired version following the [Installation +> instructions] skipping the part about the database if you want to keep it. To +> upgrade the database, apply each upgrade instructions one after another (see +> table below). + +## 2. Prerequisites + +Upgrade Zonemaster::LDNS and Zonemaster::Engine first following instructions +within the [Zonemaster::Engine installation] document. + +## 3. Upgrading Zonemaster::Backend + +To upgrade Zonemaster::Backend perform the following tasks: + + 1. stop the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + `zm_testagent` on FreeBSD) + 2. remove old files with `cpanm --uninstall Zonemaster::Backend` + 3. install any new dependencies (see corresponding upgrade document) + 4. install the latest version from CPAN with `cpanm Zonemaster::Backend` + 5. apply any remaining instructions specific to this new release + 6. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + `zm_testagent` on FreeBSD) + + +### Specific upgrade instructions + +> Always make a backup of the database before upgrading it. + +When upgrading Zonemaster::Backend, it might be needed to upgrade the database +and/or install new dependencies. Such instructions are available in the upgrade +document coming with the release. See table below to refer to the right +document. + +When upgrading from an older version than the previous release, apply each +upgrade instructions one after another. + +Current Zonemaster::Backend version | Link to instructions | Comments +------------------------------------|----------------------|----------------------- + version < 1.0.3 | [Upgrade to 1.0.3] | + 1.0.3 ≤ version < 1.1.0 | [Upgrade to 1.1.0] | + 1.1.0 ≤ version < 5.0.0 | [Upgrade to 5.0.0] | + 5.0.0 ≤ version < 5.0.2 | [Upgrade to 5.0.2] | For MySQL/MariaDB only + 5.0.2 ≤ version < 8.0.0 | [Upgrade to 8.0.0] | + + +[Installation instructions]: Installation.md +[Upgrade to 1.0.3]: upgrade/upgrade_zonemaster_backend_ver_1.0.3.md +[Upgrade to 1.1.0]: upgrade/upgrade_zonemaster_backend_ver_1.1.0.md +[Upgrade to 5.0.0]: upgrade/upgrade_zonemaster_backend_ver_5.0.0.md +[Upgrade to 5.0.2]: upgrade/upgrade_zonemaster_backend_ver_5.0.2.md +[Upgrade to 8.0.0]: upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +[Zonemaster::Engine installation]: https://github.com/zonemaster/zonemaster-engine/blob/master/docs/Installation.md diff --git a/docs/internal-documentation/maintenance/Garbage-Collection-Testing.md b/docs/internal-documentation/maintenance/Garbage-Collection-Testing.md index 392477eb3..9f3dbc2a2 100644 --- a/docs/internal-documentation/maintenance/Garbage-Collection-Testing.md +++ b/docs/internal-documentation/maintenance/Garbage-Collection-Testing.md @@ -5,31 +5,8 @@ The purpose of this instruction is to serve as a notice for manual testing of th ## Testing the unfinished tests garbage collection feature -1. Ensure that the database has the required additionnal columns for this feature: - ``` - SELECT nb_retries FROM test_results LIMIT 0; - ``` - Should return: - - ``` - nb_retries - ------------ - (0 rows) - ``` - _Remark: for MySQL use `SHOW COLUMNS FROM test_results` and ensure the `nb_retries` column is present in the list._ - -2. Check that your `/etc/zonemaster/backend_config.ini` (or, in FreeBSD, `/usr/local/etc/zonemaster/backend_config.ini`) has the proper parameter set - - Either disabled: - ``` - #maximal_number_of_retries=3 - ``` - or set to 0: - ``` - maximal_number_of_retries=0 - ``` -3. Start a test and wait for it to be finished +1. Start a test and wait for it to be finished ``` SELECT hash_id, progress FROM test_results LIMIT 1; @@ -43,12 +20,12 @@ The purpose of this instruction is to serve as a notice for manual testing of th (1 row) ``` -4. Simulate a crashed test +2. Simulate a crashed test ``` UPDATE test_results SET progress = 50, test_start_time = '2020-01-01' WHERE hash_id = '3f7a604683efaf93'; ``` -5. Check that the backend finishes the test with a result stating it was unfinished +3. Check that the backend finishes the test with a result stating it was unfinished ``` SELECT hash_id, progress FROM test_results WHERE hash_id = '3f7a604683efaf93'; @@ -61,7 +38,7 @@ The purpose of this instruction is to serve as a notice for manual testing of th (1 row) ``` -6. Ensure the test result contains the backend generated critical message: +4. Ensure the test result contains the backend generated critical message: ``` {"tag":"UNABLE_TO_FINISH_TEST","level":"CRITICAL","timestamp":"300","module":"BACKEND_TEST_AGENT"} ``` @@ -80,50 +57,3 @@ The purpose of this instruction is to serve as a notice for manual testing of th ``` -## Testing the unfinished tests garbage collection feature with a number of retries set to allow finishing tests without a critical error - -1. Set the maximal_number_of_retries parameter to a value greater than 0 in the backend_config.ini config file - ``` - maximal_number_of_retries=1 - ``` - -2. Restart the test agent in order for the parameter to be taken into account - ``` - zonemaster_backend_testagent restart - ``` - _Remark: Update accordingly to the OS where the tests are done (ex use init script for FreeBSD, etc.)_ - -3. Simulate a crashed test - ``` - UPDATE test_results SET progress = 50, test_start_time = '2020-01-01' WHERE hash_id = '3f7a604683efaf93'; - ``` - -4. Check that the backend finishes the test WITHOUT a result stating it was unfinished - - ``` - SELECT hash_id, progress FROM test_results WHERE hash_id = '3f7a604683efaf93'; - ``` - Should return a finished result: - ``` - hash_id | progress - ------------------+---------- - 3f7a604683efaf93 | 100 - (1 row) - ``` - -5. Ensure the test result does NOT contain the backend generated critical message: - ``` - {"tag":"UNABLE_TO_FINISH_TEST","level":"CRITICAL","timestamp":"300","module":"BACKEND_TEST_AGENT"} - ``` - - ``` - SELECT hash_id, progress FROM test_results WHERE hash_id = '3f7a604683efaf93' AND results::text like '%UNABLE_TO_FINISH_TEST%'; - ``` - _Remark: for MySQL queries remove the `::text` from all queries_ - Should return: - ``` - hash_id | progress - ---------+---------- - (0 rows) - - ``` diff --git a/docs/upgrade_db_zonemaster_backend_ver_1.0.3.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md similarity index 64% rename from docs/upgrade_db_zonemaster_backend_ver_1.0.3.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md index 2ba2a7764..a72af2eb0 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_1.0.3.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md @@ -1,5 +1,9 @@ -If your zonemaster database was created by a Zonemaster-Backend version smaller than -v1.0.3, and not upgraded, use the instructions in this file. +# Upgrade to 1.0.3 + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller than +v1.0.3, and not upgraded, use the following instructions. ### FreeBSD @@ -15,7 +19,7 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +perl patch/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl ``` ### PostgreSQL @@ -23,5 +27,5 @@ perl patch_mysql_db_zonemaster_backend_ver_1.0.3.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl +perl patch/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl ``` diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md new file mode 100644 index 000000000..e256eb23d --- /dev/null +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md @@ -0,0 +1,19 @@ +# Upgrade to 1.1.0 + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller than +v1.1.0, and not upgraded, use the following instructions. + +### MySQL + +```sql + ALTER TABLE test_results ADD queue INTEGER DEFAULT 0; +``` + +### PostgreSQL + +```sql + ALTER TABLE test_results ADD queue INTEGER DEFAULT 0; +``` + diff --git a/docs/upgrade_db_zonemaster_backend_ver_5.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md similarity index 62% rename from docs/upgrade_db_zonemaster_backend_ver_5.0.0.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md index 76c0c6e32..68a235d19 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_5.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md @@ -1,5 +1,9 @@ -If your zonemaster database was created by a Zonemaster-Backend version smaller than -v5.0.0, and not upgraded, use the instructions in this file. +# Upgrade to 5.0.0 + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller than +v5.0.0, and not upgraded, use the following instructions. ### FreeBSD @@ -15,7 +19,7 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_mysql_db_zonemaster_backend_ver_5.0.0.pl +perl patch/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl ``` @@ -24,7 +28,7 @@ perl patch_mysql_db_zonemaster_backend_ver_5.0.0.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +perl patch/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl ``` diff --git a/docs/upgrade_db_zonemaster_backend_ver_5.0.2.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md similarity index 68% rename from docs/upgrade_db_zonemaster_backend_ver_5.0.2.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md index e389d0596..660e63d83 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_5.0.2.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md @@ -1,5 +1,9 @@ -If your zonemaster database was created by a Zonemaster-Backend version smaller than -v5.0.2, and not upgraded, use the instructions in this file. +# Upgrade to 5.0.2 + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller than +v5.0.2, and not upgraded, use the following instructions. ### FreeBSD @@ -15,7 +19,7 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_mysql_db_zonemaster_backend_ver_5.0.2.pl +perl patch/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl ``` ### PostgreSQL diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md new file mode 100644 index 000000000..8a34ee72c --- /dev/null +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -0,0 +1,129 @@ +# Upgrade to 8.0.0 + +On FreeBSD run the following command first to become root: + +```sh +su -l +``` + +## New dependencies + +Zonemaster::Backend requires new dependencies. Depending on the used OS, run +the corresponding command. + +### Rocky Linux + +```sh +sudo dnf install perl-libintl perl-Mojolicious +``` + +```sh +sudo cpanm Plack::Middleware::ReverseProxy +``` + +### Debian / Ubuntu + +```sh +sudo apt-get install libplack-middleware-reverseproxy-perl libintl-perl libmojolicious-perl +``` + +#### Specific to Ubuntu + +```sh +sudo cpanm JSON::Validator +``` + +### FreeBSD + +```sh +pkg install p5-Plack-Middleware-ReverseProxy p5-Locale-libintl p5-Mojolicious +``` + + +## Changes in the ini file + +New sections and properties have been added to the `backend_config.ini` file. +By default the `add_api_user` method is disabled. To enable it, add the +following to your `backend_config.ini` file: + +``` +[RPCAPI] +enable_add_api_user = yes +``` + +> See the [Configuration document] for more information. + + +## Upgrading init scripts + +The `zm-rpcapi` (`zm_rpcapi` on FreeBSD) init script has been updated. It needs +to be reinstalled. + +### Rocky Linux + +``` +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi +``` + +### Debian / Ubuntu + +``` +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi +``` + +### FreeBSD + +``` +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi +``` + + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller +than v8.0.0, and not upgraded, use the following instructions. + +> Depending on the database size this upgrade can take some time (around +> 30 minutes for a database with 1 million entries) + +> You may need to run the command with `sudo`. + +### SQLite + +Run +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +perl patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl +``` + +### MySQL (or MariaDB) + +First update the privileges of the `zonemaster` user: +* Linux + ```sh + sudo mysql -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" + ``` +* FreeBSD + ```sh + mysql -u root -p -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" + ``` + + +then run +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +perl patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl +``` + +### PostgreSQL + +Run +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +perl patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl +``` + +[Configuration document]: ../Configuration.md diff --git a/docs/upgrade_db_zonemaster_backend_ver_1.1.0.md b/docs/upgrade_db_zonemaster_backend_ver_1.1.0.md deleted file mode 100644 index a8d1a2567..000000000 --- a/docs/upgrade_db_zonemaster_backend_ver_1.1.0.md +++ /dev/null @@ -1,15 +0,0 @@ -If your zonemaster database was created by a Zonemaster-Backend version smaller than -v1.1.0, and not upgraded, use the instructions in this file. - -MySQL - -``` - ALTER TABLE test_results ADD queue INTEGER DEFAULT 0; -``` - -PostgreSQL - -``` - ALTER TABLE test_results ADD queue INTEGER DEFAULT 0; -``` - diff --git a/lib/Zonemaster/Backend.pm b/lib/Zonemaster/Backend.pm index 12f18ba38..cada7ff67 100644 --- a/lib/Zonemaster/Backend.pm +++ b/lib/Zonemaster/Backend.pm @@ -1,6 +1,6 @@ package Zonemaster::Backend; -our $VERSION = '7.0.0'; +our $VERSION = '8.0.0'; use strict; use warnings; diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index b40322dc5..46ca0c348 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -13,16 +13,26 @@ use File::Slurp qw( read_file ); use Log::Any qw( $log ); use Readonly; use Zonemaster::Backend::Validator qw( :untaint ); +use Zonemaster::Backend::DB; our $path; + if ($ENV{ZONEMASTER_BACKEND_CONFIG_FILE}) { $path = $ENV{ZONEMASTER_BACKEND_CONFIG_FILE}; } -elsif ( -e '/etc/zonemaster/backend_config.ini' ) { - $path = '/etc/zonemaster/backend_config.ini'; -} else { - $path = dist_file('Zonemaster-Backend', "backend_config.ini"); + my @search_paths = ( + '/etc/zonemaster/backend_config.ini', + '/usr/local/etc/zonemaster/backend_config.ini', + dist_file('Zonemaster-Backend', "backend_config.ini") + ); + + for my $default_path (@search_paths) { + if ( -e $default_path ) { + $path = $default_path; + last; + } + } } Readonly my @SIG_NAME => split ' ', $Config{sig_name}; @@ -71,8 +81,8 @@ The configuration is interpreted according to the L. Returns a new Zonemaster::Backend::Config instance with its properties set to -values according to the given configuration with defaults according to the -configuration format. +normalized and untainted values according to the given configuration with +defaults according to the configuration format. Emits a log warning with a deprecation message for each deprecated property that is present. @@ -99,20 +109,25 @@ sub parse { my ( $class, $text ) = @_; my $obj = bless( {}, $class ); + $obj->{_public_profiles} = {}; + $obj->{_private_profiles} = {}; my $ini = Config::IniFiles->new( -file => \$text ) or die "Failed to parse config: " . join( '; ', @Config::IniFiles::errors ) . "\n"; my $get_and_clear = sub { # Read and clear a property from a Config::IniFiles object. my ( $section, $param ) = @_; - my $value = $ini->val( $section, $param ); + my ( $value, @extra ) = $ini->val( $section, $param ); + if ( @extra ) { + die "Property not unique: $section.$param\n"; + } $ini->delval( $section, $param ); return $value; }; # Validate section names { - my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'RPCAPI'); + my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'METRICS', 'RPCAPI' ); for my $section ( $ini->Sections ) { if ( !exists $sections{$section} ) { die "config: unrecognized section: $section\n"; @@ -122,21 +137,25 @@ sub parse { # Assign default values $obj->_set_DB_polling_interval( '0.5' ); + $obj->_set_MYSQL_port( '3306' ); + $obj->_set_POSTGRESQL_port( '5432' ); $obj->_set_ZONEMASTER_max_zonemaster_execution_time( '600' ); - $obj->_set_ZONEMASTER_maximal_number_of_retries( '0' ); $obj->_set_ZONEMASTER_number_of_processes_for_frontend_testing( '20' ); $obj->_set_ZONEMASTER_number_of_processes_for_batch_testing( '20' ); $obj->_set_ZONEMASTER_lock_on_queue( '0' ); $obj->_set_ZONEMASTER_age_reuse_previous_test( '600' ); $obj->_set_RPCAPI_enable_add_api_user( 'no' ); $obj->_set_RPCAPI_enable_add_batch_job( 'yes' ); + $obj->_add_LANGUAGE_locale( 'en_US' ); + $obj->_add_public_profile( 'default', undef ); + $obj->_set_METRICS_statsd_port( '8125' ); # Assign property values (part 1/2) if ( defined( my $value = $get_and_clear->( 'DB', 'engine' ) ) ) { $obj->_set_DB_engine( $value ); } - # Check required propertys (part 1/2) + # Check required properties (part 1/2) if ( !defined $obj->DB_engine ) { die "config: missing required property DB.engine\n"; } @@ -184,6 +203,11 @@ sub parse { $obj->_set_SQLITE_database_file( $value ) if $obj->DB_engine eq 'SQLite'; } + if ( defined( my $value = $ini->val( 'LANGUAGE', 'locale' ) ) ) { + if ( $value eq "" ) { + push @warnings, "Use of empty LANGUAGE.locale property is deprecated. Remove the LANGUAGE.locale entry or specify LANGUAGE.locale = en_US instead."; + } + } if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'number_of_professes_for_frontend_testing' ) ) ) { push @warnings, "Use of deprecated config property ZONEMASTER.number_of_professes_for_frontend_testing. Use ZONEMASTER.number_of_processes_for_frontend_testing instead."; @@ -202,6 +226,12 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'MYSQL', 'host' ) ) ) { $obj->_set_MYSQL_host( $value ); } + if ( defined( my $value = $get_and_clear->( 'MYSQL', 'port' ) ) ) { + if ( $obj->MYSQL_host eq 'localhost' ) { + push @warnings, "MYSQL.port is disregarded if MYSQL.host is set to 'localhost'"; + } + $obj->{_MYSQL_port} = $value; + } if ( defined( my $value = $get_and_clear->( 'MYSQL', 'user' ) ) ) { $obj->_set_MYSQL_user( $value ); } @@ -214,6 +244,9 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'POSTGRESQL', 'host' ) ) ) { $obj->_set_POSTGRESQL_host( $value ); } + if ( defined( my $value = $get_and_clear->( 'POSTGRESQL', 'port' ) ) ) { + $obj->{_POSTGRESQL_port} = $value; + } if ( defined( my $value = $get_and_clear->( 'POSTGRESQL', 'user' ) ) ) { $obj->_set_POSTGRESQL_user( $value ); } @@ -229,9 +262,6 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'max_zonemaster_execution_time' ) ) ) { $obj->_set_ZONEMASTER_max_zonemaster_execution_time( $value ); } - if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'maximal_number_of_retries' ) ) ) { - $obj->_set_ZONEMASTER_maximal_number_of_retries( $value ); - } if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'number_of_processes_for_frontend_testing' ) ) ) { $obj->_set_ZONEMASTER_number_of_processes_for_frontend_testing( $value ); } @@ -244,33 +274,35 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'age_reuse_previous_test' ) ) ) { $obj->_set_ZONEMASTER_age_reuse_previous_test( $value ); } + if ( defined( my $value = $get_and_clear->( 'METRICS', 'statsd_host' ) ) ) { + $obj->_set_METRICS_statsd_host( $value ); + } + if ( defined( my $value = $get_and_clear->( 'METRICS', 'statsd_port' ) ) ) { + $obj->_set_METRICS_statsd_port( $value ); + } if ( defined( my $value = $get_and_clear->( 'RPCAPI', 'enable_add_api_user' ) ) ) { $obj->_set_RPCAPI_enable_add_api_user( $value ); } if ( defined( my $value = $get_and_clear->( 'RPCAPI', 'enable_add_batch_job' ) ) ) { $obj->_set_RPCAPI_enable_add_batch_job( $value ); } - - $obj->{_LANGUAGE_locale} = {}; - for my $locale_tag ( split /\s+/, $get_and_clear->( 'LANGUAGE', 'locale' ) || 'en_US' ) { - $locale_tag =~ /^[a-z]{2}_[A-Z]{2}$/ - or die "Illegal locale tag in LANGUAGE.locale: $locale_tag\n"; - - !exists $obj->{_LANGUAGE_locale}{$locale_tag} - or die "Repeated locale tag in LANGUAGE.locale: $locale_tag\n"; - - $obj->{_LANGUAGE_locale}{$locale_tag} = 1; + if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { + if ( $value ne "" ) { + $obj->_reset_LANGUAGE_locale(); + for my $locale_tag ( split / +/, $value ) { + $obj->_add_LANGUAGE_locale( $locale_tag ); + } + } } - $obj->{_public_profiles} = { - default => '', - }; for my $name ( $ini->Parameters( 'PUBLIC PROFILES' ) ) { - $obj->{_public_profiles}{lc $name} = $get_and_clear->( 'PUBLIC PROFILES', $name ); + my $path = $get_and_clear->( 'PUBLIC PROFILES', $name ); + $obj->_add_public_profile( $name, $path ); } - $obj->{_private_profiles} = {}; + for my $name ( $ini->Parameters( 'PRIVATE PROFILES' ) ) { - $obj->{_private_profiles}{lc $name} = $get_and_clear->( 'PRIVATE PROFILES', $name ); + my $path = $get_and_clear->( 'PRIVATE PROFILES', $name ); + $obj->_add_private_profile( $name, $path ); } # Check required propertys (part 2/2) @@ -371,79 +403,138 @@ sub _set_DB_engine { return; } -sub _set_RPCAPI_enable_add_api_user { - my ( $self, $value ) = @_; +=head2 DB_polling_interval - $value = untaint_bool( $value ) // die "Invalid value for RPCAPI.enable_add_api_user: $value\n"; - $self->{_RPCAPI_enable_add_api_user} = $value; - return; -} +Get the value of L. -sub _set_RPCAPI_enable_add_batch_job { - my ( $self, $value ) = @_; +Returns a number. - $value = untaint_bool( $value ) // die "Invalid value for RPCAPI.enable_add_batch_job: $value\n"; - $self->{_RPCAPI_enable_add_batch_job} = $value; - return; -} +=head2 MYSQL_database +Get the value of L. -=head2 DB_polling_interval +Returns a string. -Get the value of L. +=head2 MYSQL_host -=head2 MYSQL_database +Get the value of L. -Get the value of L. +Returns a string. -=head2 MySQL_host +=head2 MYSQL_port -Get the value of L. +Returns the L +property from the loaded config. + +Returns a number. =head2 MYSQL_password Get the value of L. +Returns a string. + =head2 MYSQL_user Get the value of L. +Returns a string. + =head2 POSTGRESQL_database Get the value of L. +Returns a string. + =head2 POSTGRESQL_host Get the value of L. +Returns a string. + + +=head2 POSTGRESQL_port + +Returns the L +property from the loaded config. + +Returns a number. + =head2 POSTGRESQL_password Get the value of L. +Returns a string. + =head2 POSTGRESQL_user Get the value of L. +Returns a string. + =head2 SQLITE_database_file Get the value of L. +Returns a string. + + +=head2 LANGUAGE_locale + +Get the value of L. + +Returns a mapping from two-letter locale tag prefixes to sets of full locale +tags. +This is represented by a hash of hashrefs where all second level values are +C<1>. + +E.g.: + + ( + en => { + en_GB => 1, + en_US => 1, + }, + sv => { + sv_SE => 1, + }, + ) + + +=head2 PUBLIC_PROFILES + +Get the set of L. + +Returns a hash mapping profile names to profile paths. +The profile names are normalized to lowercase. +Profile paths are either strings or C. +C means that the Zonemaster Engine default profile should be used. + + +=head2 PRIVATE_PROFILES + +Get the set of L. + +Returns a hash mapping profile names to profile paths. +The profile names are normalized to lowercase. +Profile paths are always strings (contrast with L). + =head2 ZONEMASTER_max_zonemaster_execution_time Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_number_of_processes_for_frontend_testing @@ -451,7 +542,7 @@ Returns an integer. Get the value of L. -Returns a positive integer. +Returns a number. =head2 ZONEMASTER_number_of_processes_for_batch_testing @@ -459,7 +550,7 @@ Returns a positive integer. Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_lock_on_queue @@ -467,23 +558,33 @@ Returns an integer. Get the value of L. -Returns an integer. +Returns a number. + + +=head2 ZONEMASTER_age_reuse_previous_test + +Get the value of +L. + +Returns a number. +=cut -=head2 ZONEMASTER_maximal_number_of_retries +=head2 METRICS_statsd_host Get the value of -L. +L. -Returns an integer. +Returns a string. +=cut -=head2 ZONEMASTER_age_reuse_previous_test +=head2 METRICS_statsd_port Get the value of -L. +L. -Returns an integer. +Returns a number. =cut @@ -508,130 +609,52 @@ Return 0 or 1 # Getters for the properties documented above sub DB_polling_interval { return $_[0]->{_DB_polling_interval}; } sub MYSQL_host { return $_[0]->{_MYSQL_host}; } +sub MYSQL_port { return $_[0]->{_MYSQL_port}; } sub MYSQL_user { return $_[0]->{_MYSQL_user}; } sub MYSQL_password { return $_[0]->{_MYSQL_password}; } sub MYSQL_database { return $_[0]->{_MYSQL_database}; } sub POSTGRESQL_host { return $_[0]->{_POSTGRESQL_host}; } +sub POSTGRESQL_port { return $_[0]->{_POSTGRESQL_port}; } sub POSTGRESQL_user { return $_[0]->{_POSTGRESQL_user}; } sub POSTGRESQL_password { return $_[0]->{_POSTGRESQL_password}; } sub POSTGRESQL_database { return $_[0]->{_POSTGRESQL_database}; } sub SQLITE_database_file { return $_[0]->{_SQLITE_database_file}; } +sub LANGUAGE_locale { return %{ $_[0]->{_LANGUAGE_locale} }; } +sub PUBLIC_PROFILES { return %{ $_[0]->{_public_profiles} }; } +sub PRIVATE_PROFILES { return %{ $_[0]->{_private_profiles} }; } sub ZONEMASTER_max_zonemaster_execution_time { return $_[0]->{_ZONEMASTER_max_zonemaster_execution_time}; } -sub ZONEMASTER_maximal_number_of_retries { return $_[0]->{_ZONEMASTER_maximal_number_of_retries}; } sub ZONEMASTER_lock_on_queue { return $_[0]->{_ZONEMASTER_lock_on_queue}; } sub ZONEMASTER_number_of_processes_for_frontend_testing { return $_[0]->{_ZONEMASTER_number_of_processes_for_frontend_testing}; } sub ZONEMASTER_number_of_processes_for_batch_testing { return $_[0]->{_ZONEMASTER_number_of_processes_for_batch_testing}; } sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMASTER_age_reuse_previous_test}; } +sub METRICS_statsd_host { return $_[0]->{_METRICS_statsd_host}; } +sub METRICS_statsd_port { return $_[0]->{_METRICS_statsd_port}; } sub RPCAPI_enable_add_api_user { return $_[0]->{_RPCAPI_enable_add_api_user}; } sub RPCAPI_enable_add_batch_job { return $_[0]->{_RPCAPI_enable_add_batch_job}; } # Compile time generation of setters for the properties documented above UNITCHECK { - _create_setter( '_set_DB_polling_interval', '_DB_polling_interval' ); - _create_setter( '_set_MYSQL_host', '_MYSQL_host' ); - _create_setter( '_set_MYSQL_user', '_MYSQL_user' ); - _create_setter( '_set_MYSQL_password', '_MYSQL_password' ); - _create_setter( '_set_MYSQL_database', '_MYSQL_database' ); - _create_setter( '_set_POSTGRESQL_host', '_POSTGRESQL_host' ); - _create_setter( '_set_POSTGRESQL_user', '_POSTGRESQL_user' ); - _create_setter( '_set_POSTGRESQL_password', '_POSTGRESQL_password' ); - _create_setter( '_set_POSTGRESQL_database', '_POSTGRESQL_database' ); - _create_setter( '_set_SQLITE_database_file', '_SQLITE_database_file' ); - _create_setter( '_set_ZONEMASTER_max_zonemaster_execution_time', '_ZONEMASTER_max_zonemaster_execution_time' ); - _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries' ); - _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue' ); - _create_setter( '_set_ZONEMASTER_number_of_processes_for_frontend_testing', '_ZONEMASTER_number_of_processes_for_frontend_testing' ); - _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing' ); - _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test' ); -} - -=head2 Language_Locale_hash - -Read LANGUAGE.locale from the configuration (.ini) file and returns -the valid language tags for RPCAPI. The incoming language tag -from RPCAPI is compared to those. The language tags are mapped to -locale setting value. - -=head3 INPUT - -None - -=head3 RETURNS - -A hash of valid language tags as keys with set locale value as value. -The hash is never empty. - -=cut - -sub Language_Locale_hash { - # There is one special value to capture ambiguous (and therefore - # not permitted) translation language tags. - my ($self) = @_; - my @localetags = keys %{ $self->{_LANGUAGE_locale} }; - my %locale; - foreach my $la (@localetags) { - (my $a) = split (/_/,$la); # $a is the language code only - my $lo = "$la.UTF-8"; - # Set special value if the same language code is used more than once - # with different country codes. - if ( $locale{$a} and $locale{$a} ne $lo ) { - $locale{$a} = 'NOT-UNIQUE'; - } - else { - $locale{$a} = $lo; - } - $locale{$la} = $lo; - } - return %locale; -} - -=head2 ListLanguageTags - -Read indirectly LANGUAGE.locale from the configuration (.ini) file -and returns a list of valid language tags for RPCAPI. The list can -be retrieved via an RPCAPI method. - -=head3 INPUT - -None - -=head3 RETURNS - -An array of valid language tags. The array is never empty. - -=cut - -sub ListLanguageTags { - my ($self) = @_; - my %locale = &Language_Locale_hash($self); - my @langtags; - foreach my $key (keys %locale) { - push @langtags, $key unless $locale{$key} eq 'NOT-UNIQUE'; - } - return @langtags; -} - -sub ReadProfilesInfo { - my ($self) = @_; - - my $profiles; - foreach my $public_profile ( keys %{ $self->{_public_profiles} } ) { - $profiles->{$public_profile}->{type} = 'public'; - $profiles->{$public_profile}->{profile_file_name} = $self->{_public_profiles}{$public_profile}; - } - - foreach my $private_profile ( keys %{ $self->{_private_profiles} } ) { - $profiles->{$private_profile}->{type} = 'private'; - $profiles->{$private_profile}->{profile_file_name} = $self->{_private_profiles}{$private_profile}; - } - - return $profiles; -} - -sub ListPublicProfiles { - my ($self) = @_; - - return keys %{ $self->{_public_profiles} }; + _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_strictly_positive_millis ); + _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_host ); + _create_setter( '_set_MYSQL_port', '_MYSQL_port', \&untaint_strictly_positive_int ); + _create_setter( '_set_MYSQL_user', '_MYSQL_user', \&untaint_mariadb_user ); + _create_setter( '_set_MYSQL_password', '_MYSQL_password', \&untaint_password ); + _create_setter( '_set_MYSQL_database', '_MYSQL_database', \&untaint_mariadb_database ); + _create_setter( '_set_POSTGRESQL_host', '_POSTGRESQL_host', \&untaint_host ); + _create_setter( '_set_POSTGRESQL_port', '_POSTGRESQL_port', \&untaint_strictly_positive_int ); + _create_setter( '_set_POSTGRESQL_user', '_POSTGRESQL_user', \&untaint_postgresql_ident ); + _create_setter( '_set_POSTGRESQL_password', '_POSTGRESQL_password', \&untaint_password ); + _create_setter( '_set_POSTGRESQL_database', '_POSTGRESQL_database', \&untaint_postgresql_ident ); + _create_setter( '_set_SQLITE_database_file', '_SQLITE_database_file', \&untaint_abs_path ); + _create_setter( '_set_ZONEMASTER_max_zonemaster_execution_time', '_ZONEMASTER_max_zonemaster_execution_time', \&untaint_strictly_positive_int ); + _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue', \&untaint_non_negative_int ); + _create_setter( '_set_ZONEMASTER_number_of_processes_for_frontend_testing', '_ZONEMASTER_number_of_processes_for_frontend_testing', \&untaint_strictly_positive_int ); + _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing', \&untaint_non_negative_int ); + _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_strictly_positive_int ); + _create_setter( '_set_METRICS_statsd_host', '_METRICS_statsd_host', \&untaint_host ); + _create_setter( '_set_METRICS_statsd_port', '_METRICS_statsd_port', \&untaint_strictly_positive_int ); + _create_setter( '_set_RPCAPI_enable_add_api_user', '_RPCAPI_enable_add_api_user', \&untaint_bool ); + _create_setter( '_set_RPCAPI_enable_add_batch_job', '_RPCAPI_enable_add_batch_job', \&untaint_bool ); } =head2 new_DB @@ -655,8 +678,6 @@ A configured L object. =over 4 -=item Dies if no database engine type is defined in the configuration. - =item Dies if no adapter for the configured database engine can be loaded. =item Dies if the adapter is unable to connect to the database. @@ -666,23 +687,11 @@ A configured L object. =cut sub new_DB { - my ($self) = @_; - - # Get DB type from config - my $dbtype = $self->DB_engine; - if (!defined $dbtype) { - die "Unrecognized DB.engine in backend config"; - } - - # Load and construct DB adapter - my $dbclass = 'Zonemaster::Backend::DB::' . $dbtype; - require( join( "/", split( /::/, $dbclass ) ) . ".pm" ); - $dbclass->import(); - - my $db = $dbclass->new({ config => $self }); + my ( $self ) = @_; - # Connect or die - $db->dbh; + my $dbtype = $self->DB_engine; + my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); + my $db = $dbclass->from_config( $self ); return $db; } @@ -764,14 +773,88 @@ sub new_PM { return $pm; } -# Create a setter method with a given name using the given field +sub _add_LANGUAGE_locale { + my ( $self, $locale_tag ) = @_; + + $locale_tag = untaint_locale_tag( $locale_tag ) # + // die "Illegal locale tag in LANGUAGE.locale: $locale_tag\n"; + + my $lang_code = $locale_tag =~ s/_..$//r; + + if ( exists $self->{_LANGUAGE_locale}{$lang_code}{$locale_tag} ) { + die "Repeated locale tags in LANGUAGE.locale: $locale_tag\n"; + } + + $self->{_LANGUAGE_locale}{$lang_code}{$locale_tag} = 1; + + return; +} + +sub _reset_LANGUAGE_locale { + my ( $self ) = @_; + + delete $self->{_LANGUAGE_locale}; + return; +} + +sub _add_public_profile { + my ( $self, $name, $path ) = @_; + + $name = untaint_profile_name( $name ) # + // die "Invalid profile name in PUBLIC PROFILES section: $name\n"; + + $name = lc $name; + + if ( defined $self->{_public_profiles}{$name} || exists $self->{_private_profiles}{$name} ) { + die "Profile name not unique: $name\n"; + } + + if ( defined $path ) { + $path = untaint_abs_path( $path ) # + // die "Path must be absolute for profile: $name\n"; + } + + $self->{_public_profiles}{$name} = $path; + return; +} + +sub _add_private_profile { + my ( $self, $name, $path ) = @_; + + $name = untaint_profile_name( $name ) # + // die "Invalid profile name in PRIVATE PROFILES section: $name\n"; + + $name = lc $name; + + if ( $name eq 'default' ) { + die "Profile name must not be present in PRIVATE PROFILES section: $name\n"; + } + + if ( exists $self->{_public_profiles}{$name} || exists $self->{_private_profiles}{$name} ) { + die "Profile name not unique: $name\n"; + } + + $path = untaint_abs_path( $path ) # + // die "Path must be absolute for profile: $name\n"; + + $self->{_private_profiles}{$name} = $path; + return; +} + +# Create a setter method with a given name using the given field and validator sub _create_setter { - my ( $setter, $field ) = @_; + my ( $setter, $field, $validate ) = @_; + + $setter =~ /^_set_([A-Z_]*)_([a-z_]*)$/ + or confess "Invalid setter name"; + my $section = $1; + my $property = $2; my $setter_impl = sub { my ( $self, $value ) = @_; - $self->{$field} = $value; + $self->{$field} = $validate->( $value ) # + // die "Invalid value for $section.$property: $value\n"; return; }; @@ -785,13 +868,14 @@ sub _create_setter { sub _normalize_engine_type { my ( $value ) = @_; + # Normalized to camel case to match the database engine Perl module name, e.g. "SQLite.pm". state $db_module_names = { mysql => 'MySQL', postgresql => 'PostgreSQL', sqlite => 'SQLite', }; - return scalar $db_module_names->{ lc $value }; + return $db_module_names->{ lc $value }; } 1; diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 0e78e23d5..3898ab8cd 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -6,28 +6,91 @@ use Moose::Role; use 5.14.2; +use Digest::MD5 qw(md5_hex); +use Encode; use JSON::PP; -use Data::Dumper; +use Log::Any qw( $log ); + +use Zonemaster::Engine::Profile; +use Zonemaster::Backend::Errors; requires qw( - add_api_user_to_db add_batch_job - create_new_batch_job - create_new_test + create_db + from_config get_test_history - get_test_params process_unfinished_tests_give_up + recent_test_hash_id select_unfinished_tests test_progress test_results - user_authorized - user_exists_in_db + get_relative_start_time +); + +has 'data_source_name' => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'user' => ( + is => 'ro', + isa => 'Str', + required => 1, ); +has 'password' => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'dbhandle' => ( + is => 'rw', + isa => 'DBI::db', + required => 1, +); + +=head2 get_db_class + +Get the database adapter class for the given database type. + +Throws and exception if the database adapter class cannot be loaded. + +=cut + +sub get_db_class { + my ( $class, $db_type ) = @_; + + my $db_class = "Zonemaster::Backend::DB::$db_type"; + + require( "$db_class.pm" =~ s{::}{/}gr ); + $db_class->import(); + + return $db_class; +} + +sub dbh { + my ( $self ) = @_; + + if ( !$self->dbhandle->ping ) { + my $dbh = $self->_new_dbh( # + $self->data_source_name, + $self->user, + $self->password, + ); + + $self->dbhandle( $dbh ); + } + + return $self->dbhandle; +} + sub user_exists { my ( $self, $user ) = @_; - die "username not provided to the method user_exists\n" unless ( $user ); + die Zonemaster::Backend::Error::Internal->new( reason => "username not provided to the method user_exists") + unless ( $user ); return $self->user_exists_in_db( $user ); } @@ -35,35 +98,149 @@ sub user_exists { sub add_api_user { my ( $self, $username, $api_key ) = @_; - die "username or api_key not provided to the method add_api_user\n" - unless ( $username && $api_key ); + die Zonemaster::Backend::Error::Internal->new( reason => "username or api_key not provided to the method add_api_user") + unless ( $username && $api_key ); - die "User already exists\n" if ( $self->user_exists( $username ) ); + die Zonemaster::Backend::Error::Conflict->new( message => 'User already exists', data => { username => $username } ) + if ( $self->user_exists( $username ) ); my $result = $self->add_api_user_to_db( $username, $api_key ); - die "add_api_user_to_db not successful\n" unless ( $result ); + die Zonemaster::Backend::Error::Internal->new( reason => "add_api_user_to_db not successful") + unless ( $result ); return $result; } +# Standard SQL, can be here +sub create_new_test { + my ( $self, $domain, $test_params, $seconds_between_tests_with_same_params, $batch_id ) = @_; + + my $dbh = $self->dbh; + + $test_params->{domain} = $domain; + + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); + + my $hash_id; + + my $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; + + my $recent_hash_id = $self->recent_test_hash_id( $seconds_between_tests_with_same_params, $fingerprint ); + + if ( $recent_hash_id ) { + # A recent entry exists, so return its id + $hash_id = $recent_hash_id; + } + else { + $hash_id = substr(md5_hex(time().rand()), 0, 16); + $dbh->do( + "INSERT INTO test_results (hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?,?)", + undef, + $hash_id, + $batch_id, + $priority, + $queue_label, + $fingerprint, + $encoded_params, + $test_params->{domain}, + $undelegated, + ); + } + + return $hash_id; +} + +# Standard SQL, can be here +sub create_new_batch_job { + my ( $self, $username ) = @_; + + my $dbh = $self->dbh; + my ( $batch_id, $creation_time ) = $dbh->selectrow_array( " + SELECT + batch_id, + batch_jobs.creation_time AS batch_creation_time + FROM + test_results + JOIN batch_jobs + ON batch_id = batch_jobs.id + AND username = ? + WHERE + test_results.progress <> 100 + LIMIT 1 + ", undef, $username ); + + die Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) + if ( $batch_id ); + + $dbh->do( "INSERT INTO batch_jobs (username) VALUES (?)", undef, $username ); + my $new_batch_id = $dbh->last_insert_id( undef, undef, "batch_jobs", undef ); + + return $new_batch_id; +} + +# Standard SQL, can be here +sub user_exists_in_db { + my ( $self, $user ) = @_; + + my $dbh = $self->dbh; + my ( $id ) = $dbh->selectrow_array( + "SELECT id FROM users WHERE username = ?", + undef, + $user + ); + + return $id; +} + +# Standard SQL, can be here +sub add_api_user_to_db { + my ( $self, $user_name, $api_key ) = @_; + + my $dbh = $self->dbh; + my $nb_inserted = $dbh->do( + "INSERT INTO users (username, api_key) VALUES (?,?)", + undef, + $user_name, + $api_key, + ); + + return $nb_inserted; +} + +# Standard SQL, can be here +sub user_authorized { + my ( $self, $user, $api_key ) = @_; + + my $dbh = $self->dbh; + my ( $id ) = $dbh->selectrow_array( + "SELECT id FROM users WHERE username = ? AND api_key = ?", + undef, + $user, + $api_key + ); + + return $id; +} + # Standard SQL, can be here sub get_test_request { - my ( $self ) = @_; + my ( $self, $queue_label ) = @_; my $result_id; my $dbh = $self->dbh; - - + my ( $id, $hash_id ); - my $lock_on_queue = $self->config->ZONEMASTER_lock_on_queue; - if ( defined $lock_on_queue ) { - ( $id, $hash_id ) = $dbh->selectrow_array( qq[ SELECT id, hash_id FROM test_results WHERE progress=0 AND queue=? ORDER BY priority DESC, id ASC LIMIT 1 ], undef, $lock_on_queue ); + if ( defined $queue_label ) { + ( $id, $hash_id ) = $dbh->selectrow_array( qq[ SELECT id, hash_id FROM test_results WHERE progress=0 AND queue=? ORDER BY priority DESC, id ASC LIMIT 1 ], undef, $queue_label ); } else { ( $id, $hash_id ) = $dbh->selectrow_array( q[ SELECT id, hash_id FROM test_results WHERE progress=0 ORDER BY priority DESC, id ASC LIMIT 1 ] ); } - + if ($id) { $dbh->do( q[UPDATE test_results SET progress=1 WHERE id=?], undef, $id ); $result_id = $hash_id; @@ -71,63 +248,229 @@ sub get_test_request { return $result_id; } +# Standard SQL, can be here +sub get_test_params { + my ( $self, $test_id ) = @_; + + my $dbh = $self->dbh; + my ( $params_json ) = $dbh->selectrow_array( "SELECT params FROM test_results WHERE hash_id = ?", undef, $test_id ); + + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $params_json; + + my $result; + eval { + # TODO: do we use "encode_utf8" as this was the case in PostgreSQL + # (see commit diff) + $result = decode_json( $params_json ); + }; + + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; + + return $result; +} + # Standatd SQL, can be here sub get_batch_job_result { - my ( $self, $batch_id ) = @_; - - my $dbh = $self->dbh; - - my %result; - $result{nb_running} = 0; - $result{nb_finished} = 0; - - my $query = " - SELECT hash_id, progress - FROM test_results - WHERE batch_id=?"; - - my $sth1 = $dbh->prepare( $query ); - $sth1->execute( $batch_id ); - while ( my $h = $sth1->fetchrow_hashref ) { - if ( $h->{progress} eq '100' ) { - $result{nb_finished}++; - push(@{$result{finished_test_ids}}, $h->{hash_id}); - } - else { - $result{nb_running}++; - } - } - - return \%result; + my ( $self, $batch_id ) = @_; + + my $dbh = $self->dbh; + + my %result; + $result{nb_running} = 0; + $result{nb_finished} = 0; + + my $query = " + SELECT hash_id, progress + FROM test_results + WHERE batch_id=?"; + + my $sth1 = $dbh->prepare( $query ); + $sth1->execute( $batch_id ); + while ( my $h = $sth1->fetchrow_hashref ) { + if ( $h->{progress} eq '100' ) { + $result{nb_finished}++; + push(@{$result{finished_test_ids}}, $h->{hash_id}); + } + else { + $result{nb_running}++; + } + } + + return \%result; } sub process_unfinished_tests { - my ( $self ) = @_; - - my $sth1 = $self->select_unfinished_tests(); - + my ( $self, $queue_label, $test_run_timeout ) = @_; + + my $sth1 = $self->select_unfinished_tests( # + $queue_label, + $test_run_timeout, + ); + while ( my $h = $sth1->fetchrow_hashref ) { - if ( $h->{nb_retries} < $self->config->ZONEMASTER_maximal_number_of_retries ) { - $self->schedule_for_retry($h->{hash_id}); + $self->force_end_test($h->{hash_id}, $h->{results}, $test_run_timeout); + } +} + +sub force_end_test { + my ( $self, $hash_id, $results, $timestamp ) = @_; + my $result; + if ( defined $results && $results =~ /^\[/ ) { + $result = decode_json( $results ); + } + else { + $result = []; + } + push @$result, + { + "level" => "CRITICAL", + "module" => "BACKEND_TEST_AGENT", + "tag" => "UNABLE_TO_FINISH_TEST", + "timestamp" => $timestamp, + }; + $self->process_unfinished_tests_give_up($result, $hash_id); +} + +sub process_dead_test { + my ( $self, $hash_id ) = @_; + my ( $results ) = $self->dbh->selectrow_array("SELECT results FROM test_results WHERE hash_id = ?", undef, $hash_id); + $self->force_end_test($hash_id, $results, $self->get_relative_start_time($hash_id)); +} + +# A thin wrapper around DBI->connect to ensure similar behavior across database +# engines. +sub _new_dbh { + my ( $class, $data_source_name, $user, $password ) = @_; + + if ( $user ) { + $log->noticef( "Connecting to database '%s' as user '%s'", $data_source_name, $user ); + } + else { + $log->noticef( "Connecting to database '%s'", $data_source_name ); + } + + my $dbh = DBI->connect( + $data_source_name, + $user, + $password, + { + RaiseError => 1, + AutoCommit => 1, } - else { - my $result; - if ( defined $h->{results} && $h->{results} =~ /^\[/ ) { - $result = decode_json( $h->{results} ); - } - else { - $result = []; - } - push @$result, - { - "level" => "CRITICAL", - "module" => "BACKEND_TEST_AGENT", - "tag" => "UNABLE_TO_FINISH_TEST", - "timestamp" => $self->config->ZONEMASTER_max_zonemaster_execution_time - }; - $self->process_unfinished_tests_give_up($result, $h->{hash_id}); + ); + + $dbh->{AutoInactiveDestroy} = 1; + + return $dbh; +} + +sub _project_params { + my ( $self, $params ) = @_; + + my $profile = Zonemaster::Engine::Profile->effective; + + my %projection = (); + + $projection{domain} = lc $$params{domain} // ""; + $projection{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); + $projection{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); + $projection{profile} = $$params{profile} // "default"; + + my $array_ds_info = $$params{ds_info} // []; + my @array_ds_info_sort = sort { + $a->{algorithm} cmp $b->{algorithm} or + $a->{digest} cmp $b->{digest} or + $a->{digtype} <=> $b->{digtype} or + $a->{keytag} <=> $b->{keytag} + } @$array_ds_info; + + $projection{ds_info} = \@array_ds_info_sort; + + my $array_nameservers = $$params{nameservers} // []; + for my $nameserver (@$array_nameservers) { + if ( defined $$nameserver{ip} and $$nameserver{ip} eq "" ) { + delete $$nameserver{ip}; } + $$nameserver{ns} = lc $$nameserver{ns}; } + my @array_nameservers_sort = sort { + $a->{ns} cmp $b->{ns} or + ( defined $a->{ip} and defined $b->{ip} and $a->{ip} cmp $b->{ip} ) + } @$array_nameservers; + + $projection{nameservers} = \@array_nameservers_sort; + + return \%projection; +} + +sub _params_to_json_str { + my ( $self, $params ) = @_; + + my $js = JSON::PP->new; + $js->canonical( 1 ); + + my $encoded_params = $js->encode( $params ); + + return $encoded_params; +} + +=head2 encode_params + +Encode the params object into a JSON string. First a projection of some +parameters is performed then all additional properties are kept. +Returns a JSON string of a the using a union of the given hash and its +normalization using default values, see +L + +=cut + +sub encode_params { + my ( $self, $params ) = @_; + + my $projected_params = $self->_project_params( $params ); + $params = { %$params, %$projected_params }; + my $encoded_params = $self->_params_to_json_str( $params ); + + return $encoded_params; +} + +=head2 generate_fingerprint + +Returns a fingerprint of the hash passed in argument. +The fingerprint is computed after projecting the hash. +Such fingerprint are usefull to find similar tests in the database. + +=cut + +sub generate_fingerprint { + my ( $self, $params ) = @_; + + my $projected_params = $self->_project_params( $params ); + my $encoded_params = $self->_params_to_json_str( $projected_params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + + return $fingerprint; +} + + +=head2 undelegated + +Returns the value 1 if the test to be created is if type undelegated, +else value 0. The test is considered to be undelegated if the "ds_info" or +"nameservers" parameters is are defined with data after projection. + +=cut + +sub undelegated { + my ( $self, $params ) = @_; + + my $projected_params = $self->_project_params( $params ); + + return 1 if defined( $$projected_params{ds_info}[0] ); + return 1 if defined( $$projected_params{nameservers}[0] ); + return 0; } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ca7467475..170aaa510 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -5,165 +5,154 @@ our $VERSION = '1.1.0'; use Moose; use 5.14.2; -use Data::Dumper; use DBI qw(:utils); use Digest::MD5 qw(md5_hex); -use Encode; use JSON::PP; -use Log::Any qw($log); -use Zonemaster::Backend::Config; +use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); +use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -has 'config' => ( - is => 'ro', - isa => 'Zonemaster::Backend::Config', - required => 1, -); +=head1 CLASS METHODS -has 'dbhandle' => ( - is => 'rw', - isa => 'DBI::db', -); +=head2 from_config -sub dbh { - my ( $self ) = @_; - my $dbh = $self->dbhandle; +Construct a new instance from a Zonemaster::Backend::Config. - if ( $dbh and $dbh->ping ) { - return $dbh; - } - else { - my $database = $self->config->MYSQL_database; - my $host = $self->config->MYSQL_host; - my $user = $self->config->MYSQL_user(); - my $password = $self->config->MYSQL_password(); - - $log->notice( "Connecting to MySQL: database=$database host=$host user=$user" ) if $log->is_notice; - $dbh = DBI->connect( - "DBI:mysql:database=$database;host=$host", - $user, - $password, - { - RaiseError => 1, - AutoCommit => 1 - } - ); + my $db = Zonemaster::Backend::DB::MySQL->from_config( $config ); - $dbh->{AutoInactiveDestroy} = 1; - $self->dbhandle( $dbh ); - return $dbh; - } -} +=cut -sub user_exists_in_db { - my ( $self, $user ) = @_; +sub from_config { + my ( $class, $config ) = @_; - my ( $id ) = $self->dbh->selectrow_array( "SELECT id FROM users WHERE username = ?", undef, $user ); + my $database = $config->MYSQL_database; + my $host = $config->MYSQL_host; + my $port = $config->MYSQL_port; + my $user = $config->MYSQL_user; + my $password = $config->MYSQL_password; - return $id; -} + if ( untaint_ipv6_address( $host ) ) { + $host = "[$host]"; + } -sub add_api_user_to_db { - my ( $self, $user_name, $api_key ) = @_; + my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; - my $nb_inserted = $self->dbh->do( - "INSERT INTO users (user_info, username, api_key) VALUES (?,?,?)", - undef, - 'NULL', - $user_name, - $api_key, + my $dbh = $class->_new_dbh( + $data_source_name, + $user, + $password, ); - return $nb_inserted; -} - -sub user_authorized { - my ( $self, $user, $api_key ) = @_; - - my ( $id ) = - $self->dbh->selectrow_array( q[SELECT id FROM users WHERE username = ? AND api_key = ?], undef, $user, $api_key ); - - return $id; + return $class->new( + { + data_source_name => $data_source_name, + user => $user, + password => $password, + dbhandle => $dbh, + } + ); } -sub create_new_batch_job { - my ( $self, $username ) = @_; - - my ( $batch_id, $creaton_time ) = $self->dbh->selectrow_array( " - SELECT - batch_id, - batch_jobs.creation_time AS batch_creation_time - FROM - test_results - JOIN batch_jobs - ON batch_id=batch_jobs.id - AND username=? - WHERE - test_results.progress<>100 - LIMIT 1 - ", undef, $username ); - - die "You can't create a new batch job, job:[$batch_id] started on:[$creaton_time] still running \n" if ( $batch_id ); - - $self->dbh->do( "INSERT INTO batch_jobs (username) VALUES(?)", undef, $username ); - my ( $new_batch_id ) = $self->dbh->{mysql_insertid}; - - return $new_batch_id; -} -sub create_new_test { - my ( $self, $domain, $test_params, $seconds_between_tests_with_same_params, $batch_id ) = @_; +sub create_db { + my ( $self ) = @_; my $dbh = $self->dbh; - $test_params->{domain} = $domain; - my $js = JSON::PP->new->canonical; - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( $encoded_params ); - my $result_id; + #################################################################### + # TEST RESULTS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS test_results ( + id integer AUTO_INCREMENT PRIMARY KEY, + hash_id VARCHAR(16) NOT NULL, + domain varchar(255) NOT NULL, + batch_id integer NULL, + creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + test_start_time TIMESTAMP NULL DEFAULT NULL, + test_end_time TIMESTAMP NULL DEFAULT NULL, + priority integer DEFAULT 10, + queue integer DEFAULT 0, + progress integer DEFAULT 0, + fingerprint character varying(32), + params blob NOT NULL, + results mediumblob DEFAULT NULL, + undelegated integer NOT NULL DEFAULT 0 + ) ENGINE=InnoDB + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'test_results' table", data => $dbh->errstr() ); + + # Manually create the index if it does not exist + # the clause IF NOT EXISTS is not available for MySQL (used with FreeBSD) + + # retrieve all indexes by key name + my $indexes = $dbh->selectall_hashref( 'SHOW INDEXES FROM test_results', 'Key_name' ); + + if ( not exists($indexes->{test_results__hash_id}) ) { + $dbh->do( + 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' + ); + } + if ( not exists($indexes->{test_results__fingerprint}) ) { + $dbh->do( + 'CREATE INDEX test_results__fingerprint ON test_results (fingerprint)' + ); + } + if ( not exists($indexes->{test_results__batch_id_progress}) ) { + $dbh->do( + 'CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)' + ); + } + if ( not exists($indexes->{test_results__progress}) ) { + $dbh->do( + 'CREATE INDEX test_results__progress ON test_results (progress)' + ); + } + if ( not exists($indexes->{test_results__domain_undelegated}) ) { + $dbh->do( + 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' + ); + } + - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + #################################################################### + # BATCH JOBS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS batch_jobs ( + id integer AUTO_INCREMENT PRIMARY KEY, + username character varying(50) NOT NULL, + creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL + ) ENGINE=InnoDB; + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'batch_jobs' table", data => $dbh->errstr() ); + + + #################################################################### + # USERS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS users ( + id integer AUTO_INCREMENT primary key, + username varchar(128), + api_key varchar(512) + ) ENGINE=InnoDB; + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); +} - eval { - $dbh->do( q[LOCK TABLES test_results WRITE] ); - my ( $recent_hash_id ) = $dbh->selectrow_array( - q[ - SELECT hash_id FROM test_results WHERE params_deterministic_hash = ? AND (TO_SECONDS(NOW()) - TO_SECONDS(creation_time)) < ? - ], - undef, $test_params_deterministic_hash, $seconds_between_tests_with_same_params, - ); +sub recent_test_hash_id { + my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; - if ( $recent_hash_id ) { - # A recent entry exists, so return its id - $result_id = $recent_hash_id; - } - else { - $dbh->do( - q[ - INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params, domain, test_start_time, undelegated) VALUES (?, ?,?,?,?,?, NOW(),?) - ], - undef, - $batch_id, - $priority, - $queue, - $test_params_deterministic_hash, - $encoded_params, - $test_params->{domain}, - ($test_params->{nameservers})?(1):(0), - ); - - my ( undef, $hash_id ) = $dbh->selectrow_array( - "SELECT id, hash_id FROM test_results WHERE params_deterministic_hash=? ORDER BY id DESC LIMIT 1", undef, $test_params_deterministic_hash); - - $result_id = $hash_id; - } - }; - $dbh->do( q[UNLOCK TABLES] ); + my $dbh = $self->dbh; + my ( $recent_hash_id ) = $dbh->selectrow_array( + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND (TO_SECONDS(NOW()) - TO_SECONDS(creation_time)) < ?", + undef, $fingerprint, $age_reuse_previous_test + ); - return $result_id; + return $recent_hash_id; } sub test_progress { @@ -184,21 +173,6 @@ sub test_progress { return $result; } -sub get_test_params { - my ( $self, $test_id ) = @_; - - my ( $params_json ) = $self->dbh->selectrow_array( "SELECT params FROM test_results WHERE hash_id=?", undef, $test_id ); - - my $result; - eval { - $result = decode_json( $params_json ); - }; - - warn "decoding of params_json failed (testi_id: [$test_id]):".Dumper($params_json) if $@; - - return decode_json( $params_json ); -} - sub test_results { my ( $self, $test_id, $new_results ) = @_; @@ -210,8 +184,22 @@ sub test_results { my $result; my ( $hrefs ) = $self->dbh->selectall_hashref( "SELECT id, hash_id, CONVERT_TZ(`creation_time`, \@\@session.time_zone, '+00:00') AS creation_time, params, results FROM test_results WHERE hash_id=?", 'hash_id', undef, $test_id ); $result = $hrefs->{$test_id}; - $result->{params} = decode_json( $result->{params} ); - $result->{results} = decode_json( $result->{results} ); + + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $result; + + eval { + $result->{params} = decode_json( $result->{params} ); + + if (defined $result->{results}) { + $result->{results} = decode_json( $result->{results} ); + } else { + $result->{results} = []; + } + }; + + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } @@ -236,6 +224,7 @@ sub get_test_history { hash_id, CONVERT_TZ(`creation_time`, @@session.time_zone, '+00:00') AS creation_time, params, + undelegated, results FROM test_results @@ -252,6 +241,7 @@ sub get_test_history { hash_id, CONVERT_TZ(`creation_time`, @@session.time_zone, '+00:00') AS creation_time, params, + undelegated, results FROM test_results @@ -272,7 +262,7 @@ sub get_test_history { my $warning = ( grep { $_->{level} eq 'WARNING' } @{ $h->{results} } ); # More important overwrites - my $overall = 'INFO'; + my $overall = 'ok'; $overall = 'warning' if $warning; $overall = 'error' if $error; $overall = 'critical' if $critical; @@ -282,6 +272,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall, } ); @@ -295,77 +286,74 @@ sub add_batch_job { my $batch_id; my $dbh = $self->dbh; - my $js = JSON::PP->new; - $js->canonical( 1 ); if ( $self->user_authorized( $params->{username}, $params->{api_key} ) ) { $batch_id = $self->create_new_batch_job( $params->{username} ); my $test_params = $params->{test_params}; - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + my $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; $dbh->{AutoCommit} = 0; eval {$dbh->do( "DROP INDEX test_results__hash_id ON test_results" );}; - eval {$dbh->do( "DROP INDEX test_results__params_deterministic_hash ON test_results" );}; + eval {$dbh->do( "DROP INDEX test_results__fingerprint ON test_results" );}; eval {$dbh->do( "DROP INDEX test_results__batch_id_progress ON test_results" );}; eval {$dbh->do( "DROP INDEX test_results__progress ON test_results" );}; eval {$dbh->do( "DROP INDEX test_results__domain_undelegated ON test_results" );}; - - my $sth = $dbh->prepare( 'INSERT INTO test_results (domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?) ' ); + + my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, fingerprint, params, undelegated) VALUES (?,?,?,?,?,?,?,?)' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( encode_utf8( $encoded_params ) ); - $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue, $test_params_deterministic_hash, $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); + + my $hash_id = substr(md5_hex(time().rand()), 0, 16); + $sth->execute( $hash_id, $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); } $dbh->do( "CREATE INDEX test_results__hash_id ON test_results (hash_id, creation_time)" ); - $dbh->do( "CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)" ); + $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); $dbh->do( "CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)" ); $dbh->do( "CREATE INDEX test_results__progress ON test_results (progress)" ); $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); - + $dbh->commit(); $dbh->{AutoCommit} = 1; } else { - die "User $params->{username} not authorized to use batch mode\n"; + die Zonemaster::Backend::Error::PermissionDenied->new( message => 'User not authorized to use batch mode', data => { username => $params->{username}} ); } return $batch_id; } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < DATE_SUB(NOW(), INTERVAL ? SECOND) - AND nb_retries <= ? AND progress > 0 AND progress < 100 AND queue = ?" ); $sth->execute( # - $self->config->ZONEMASTER_max_zonemaster_execution_time, - $self->config->ZONEMASTER_maximal_number_of_retries, - $self->config->ZONEMASTER_lock_on_queue, + $test_run_timeout, + $queue_label, ); return $sth; } else { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < DATE_SUB(NOW(), INTERVAL ? SECOND) - AND nb_retries <= ? AND progress > 0 AND progress < 100" ); $sth->execute( # - $self->config->ZONEMASTER_max_zonemaster_execution_time, - $self->config->ZONEMASTER_maximal_number_of_retries, + $test_run_timeout, ); return $sth; } @@ -377,10 +365,10 @@ sub process_unfinished_tests_give_up { $self->dbh->do("UPDATE test_results SET progress = 100, test_end_time = NOW(), results = ? WHERE hash_id=?", undef, encode_json($result), $hash_id); } -sub schedule_for_retry { +sub get_relative_start_time { my ( $self, $hash_id ) = @_; - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); + return $self->dbh->selectrow_array("SELECT now() - test_start_time FROM test_results WHERE hash_id=?", undef, $hash_id); } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 570993af8..f987c76eb 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -9,81 +9,136 @@ use DBI qw(:utils); use Digest::MD5 qw(md5_hex); use Encode; use JSON::PP; -use Log::Any qw($log); use Zonemaster::Backend::DB; -use Zonemaster::Backend::Config; +use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -has 'config' => ( - is => 'ro', - isa => 'Zonemaster::Backend::Config', - required => 1, -); +=head1 CLASS METHODS -has 'dbhandle' => ( - is => 'rw', - isa => 'DBI::db', -); +=head2 from_config -sub dbh { - my ( $self ) = @_; - my $dbh = $self->dbhandle; +Construct a new instance from a Zonemaster::Backend::Config. - if ( $dbh and $dbh->ping ) { - return $dbh; - } - else { - my $database = $self->config->POSTGRESQL_database; - my $host = $self->config->POSTGRESQL_host; - my $user = $self->config->POSTGRESQL_user; - my $password = $self->config->POSTGRESQL_password; - - $log->notice( "Connecting to PostgreSQL: database=$database host=$host user=$user" ) if $log->is_notice; - $dbh = DBI->connect( - "DBI:Pg:database=$database;host=$host", - $user, - $password, - { - RaiseError => 1, - AutoCommit => 1 - } - ); + my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); - $dbh->{AutoInactiveDestroy} = 1; - $self->dbhandle( $dbh ); - return $dbh; - } -} +=cut -sub user_exists_in_db { - my ( $self, $user ) = @_; +sub from_config { + my ( $class, $config ) = @_; - my $dbh = $self->dbh; - my ( $id ) = $dbh->selectrow_array( "SELECT id FROM users WHERE user_info->>'username'=?", undef, $user ); + my $database = $config->POSTGRESQL_database; + my $host = $config->POSTGRESQL_host; + my $port = $config->POSTGRESQL_port; + my $user = $config->POSTGRESQL_user; + my $password = $config->POSTGRESQL_password; + + my $data_source_name = "DBI:Pg:dbname=$database;host=$host;port=$port"; + + my $dbh = $class->_new_dbh( + $data_source_name, + $user, + $password, + ); - return $id; + return $class->new( + { + data_source_name => $data_source_name, + user => $user, + password => $password, + dbhandle => $dbh, + } + ); } -sub add_api_user_to_db { - my ( $self, $user_name, $api_key ) = @_; +sub create_db { + my ( $self ) = @_; my $dbh = $self->dbh; - my $nb_inserted = $dbh->do( "INSERT INTO users (user_info) VALUES (?)", undef, encode_json( { username => $user_name, api_key => $api_key } ) ); - return $nb_inserted; + #################################################################### + # TEST RESULTS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS test_results ( + id serial PRIMARY KEY, + hash_id VARCHAR(16) NOT NULL, + domain VARCHAR(255) NOT NULL, + batch_id integer, + creation_time timestamp without time zone DEFAULT NOW() NOT NULL, + test_start_time timestamp without time zone DEFAULT NULL, + test_end_time timestamp without time zone DEFAULT NULL, + priority integer DEFAULT 10, + queue integer DEFAULT 0, + progress integer DEFAULT 0, + fingerprint varchar(32), + params json NOT NULL, + undelegated integer NOT NULL DEFAULT 0, + results json + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL error, could not create 'test_results' table", data => $dbh->errstr() ); + + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__hash_id ON test_results (hash_id)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__fingerprint ON test_results (fingerprint)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__batch_id_progress ON test_results (batch_id, progress)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__progress ON test_results (progress)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__domain_undelegated ON test_results (domain, undelegated)' + ); + # this index helps speed up query time to retrieve the next test to + # perform when using batches + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__progress_priority_id ON test_results (progress, priority DESC, id) WHERE (progress = 0)' + ); + + + #################################################################### + # BATCH JOBS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS batch_jobs ( + id serial PRIMARY KEY, + username varchar(50) NOT NULL, + creation_time timestamp without time zone DEFAULT NOW() NOT NULL + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL error, could not create 'batch_jobs' table", data => $dbh->errstr() ); + + + #################################################################### + # USERS + #################################################################### + $dbh->do( + 'CREATE TABLE IF NOT EXISTS users ( + id serial PRIMARY KEY, + username VARCHAR(128), + api_key VARCHAR(512) + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL error, could not create 'users' table", data => $dbh->errstr() ); + } -sub user_authorized { - my ( $self, $user, $api_key ) = @_; +sub recent_test_hash_id { + my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; my $dbh = $self->dbh; - my $id = - $dbh->selectrow_array( "SELECT id FROM users WHERE user_info->>'username'=? AND user_info->>'api_key'=?", - undef, $user, $api_key ); + my ( $recent_hash_id ) = $dbh->selectrow_array( + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND creation_time > NOW() - ?::interval", + undef, $fingerprint, $age_reuse_previous_test + ); - return $id; + return $recent_hash_id; } sub test_progress { @@ -98,83 +153,8 @@ sub test_progress { $dbh->do( "UPDATE test_results SET progress=? WHERE hash_id=? AND progress <> 100", undef, $progress, $test_id ); } } - - my ( $result ) = $dbh->selectrow_array( "SELECT progress FROM test_results WHERE hash_id=?", undef, $test_id ); - - return $result; -} -sub create_new_batch_job { - my ( $self, $username ) = @_; - - my $dbh = $self->dbh; - my ( $batch_id, $creation_time ) = $dbh->selectrow_array( " - SELECT - batch_id, - batch_jobs.creation_time AS batch_creation_time - FROM - test_results - JOIN batch_jobs - ON batch_id=batch_jobs.id - AND username=? WHERE - test_results.progress<>100 - LIMIT 1 - ", undef, $username ); - - die "You can't create a new batch job, job:[$batch_id] started on:[$creation_time] still running \n" if ( $batch_id ); - - my ( $new_batch_id ) = - $dbh->selectrow_array( "INSERT INTO batch_jobs (username) VALUES (?) RETURNING id", undef, $username ); - - return $new_batch_id; -} - -sub create_new_test { - my ( $self, $domain, $test_params, $seconds_between_tests_with_same_params, $batch_id ) = @_; - my $dbh = $self->dbh; - - $test_params->{domain} = $domain; - my $js = JSON::PP->new; - $js->canonical( 1 ); - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( encode_utf8( $encoded_params ) ); - - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; - - my $sth = $dbh->prepare( " - INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params) - SELECT ?, ?, ?, ?, ? - WHERE NOT EXISTS ( - SELECT * FROM test_results - WHERE params_deterministic_hash = ? - AND creation_time > NOW() - ?::interval - )" ); - my $nb_inserted = $sth->execute( # - $batch_id, - $priority, - $queue, - $test_params_deterministic_hash, - $encoded_params, - $test_params_deterministic_hash, - sprintf( "%d seconds", $seconds_between_tests_with_same_params ), - ); - - my ( undef, $hash_id ) = $dbh->selectrow_array( - "SELECT id,hash_id FROM test_results WHERE params_deterministic_hash=? ORDER BY id DESC LIMIT 1", undef, $test_params_deterministic_hash ); - - return $hash_id; -} - -sub get_test_params { - my ( $self, $test_id ) = @_; - - my $result; - - my $dbh = $self->dbh; - my ( $params_json ) = $dbh->selectrow_array( "SELECT params FROM test_results WHERE hash_id=?", undef, $test_id ); - eval { $result = decode_json( encode_utf8( $params_json ) ); }; - die "$@ \n" if $@; + my ( $result ) = $dbh->selectrow_array( "SELECT progress FROM test_results WHERE hash_id=?", undef, $test_id ); return $result; } @@ -188,27 +168,36 @@ sub test_results { if ( $results ); my $result; + my ( $hrefs ) = $dbh->selectall_hashref( "SELECT id, hash_id, creation_time at time zone current_setting('TIMEZONE') at time zone 'UTC' as creation_time, params, results FROM test_results WHERE hash_id=?", 'hash_id', undef, $test_id ); + $result = $hrefs->{$test_id}; + + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $result; + eval { - my ( $hrefs ) = $dbh->selectall_hashref( "SELECT id, hash_id, creation_time at time zone current_setting('TIMEZONE') at time zone 'UTC' as creation_time, params, results FROM test_results WHERE hash_id=?", 'hash_id', undef, $test_id ); - $result = $hrefs->{$test_id}; - - # This workaround is needed to properly handle all versions of perl and the DBD::Pg module + # This workaround is needed to properly handle all versions of perl and the DBD::Pg module # More details in the zonemaster backend issue #570 if (utf8::is_utf8($result->{params}) ) { - $result->{params} = decode_json( encode_utf8($result->{params}) ); + $result->{params} = decode_json( encode_utf8($result->{params}) ); } else { - $result->{params} = decode_json( $result->{params} ); + $result->{params} = decode_json( $result->{params} ); } - - if (utf8::is_utf8($result->{results} ) ) { + + if (defined $result->{results} ) { + if (utf8::is_utf8($result->{results} ) ) { $result->{results} = decode_json( encode_utf8($result->{results}) ); - } - else { + } + else { $result->{results} = decode_json( $result->{results} ); + } + } else { + $result->{results} = []; } }; - die "$@ \n" if $@; + + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id }) + if $@; return $result; } @@ -220,23 +209,24 @@ sub get_test_history { my $undelegated = ""; if ($p->{filter} eq "undelegated") { - $undelegated = "AND (params->'nameservers') IS NOT NULL"; + $undelegated = "AND undelegated = 1"; } elsif ($p->{filter} eq "delegated") { - $undelegated = "AND (params->'nameservers') IS NULL"; + $undelegated = "AND undelegated = 0"; } my @results; my $query = " - SELECT + SELECT (SELECT count(*) FROM (SELECT json_array_elements(results) AS result) AS t1 WHERE result->>'level'='CRITICAL') AS nb_critical, (SELECT count(*) FROM (SELECT json_array_elements(results) AS result) AS t1 WHERE result->>'level'='ERROR') AS nb_error, (SELECT count(*) FROM (SELECT json_array_elements(results) AS result) AS t1 WHERE result->>'level'='WARNING') AS nb_warning, id, hash_id, + undelegated, creation_time at time zone current_setting('TIMEZONE') at time zone 'UTC' as creation_time FROM test_results - WHERE params->>'domain'=" . $dbh->quote( $p->{frontend_params}->{domain} ) . " $undelegated - ORDER BY id DESC + WHERE domain=" . $dbh->quote( $p->{frontend_params}->{domain} ) . " $undelegated + ORDER BY id DESC OFFSET $p->{offset} LIMIT $p->{limit}"; my $sth1 = $dbh->prepare( $query ); $sth1->execute; @@ -257,6 +247,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall_result, } ); @@ -270,80 +261,77 @@ sub add_batch_job { my $batch_id; my $dbh = $self->dbh; - my $js = JSON::PP->new; - $js->canonical( 1 ); if ( $self->user_authorized( $params->{username}, $params->{api_key} ) ) { $batch_id = $self->create_new_batch_job( $params->{username} ); my $test_params = $params->{test_params}; - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + my $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; $dbh->begin_work(); $dbh->do( "ALTER TABLE test_results DROP CONSTRAINT IF EXISTS test_results_pkey" ); $dbh->do( "DROP INDEX IF EXISTS test_results__hash_id" ); - $dbh->do( "DROP INDEX IF EXISTS test_results__params_deterministic_hash" ); + $dbh->do( "DROP INDEX IF EXISTS test_results__fingerprint" ); $dbh->do( "DROP INDEX IF EXISTS test_results__batch_id_progress" ); $dbh->do( "DROP INDEX IF EXISTS test_results__progress" ); $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); - - $dbh->do( "COPY test_results(batch_id, priority, queue, params_deterministic_hash, params) FROM STDIN" ); + + $dbh->do( "COPY test_results(hash_id,domain ,batch_id, priority, queue, fingerprint, params, undelegated) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( encode_utf8( $encoded_params ) ); - $dbh->pg_putcopydata("$batch_id\t$priority\t$queue\t$test_params_deterministic_hash\t$encoded_params\n"); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); + + my $hash_id = substr(md5_hex(time().rand()), 0, 16); + $dbh->pg_putcopydata("$hash_id\t$test_params->{domain}\t$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\t$undelegated\n"); } $dbh->pg_putcopyend(); $dbh->do( "ALTER TABLE test_results ADD PRIMARY KEY (id)" ); $dbh->do( "CREATE INDEX test_results__hash_id ON test_results (hash_id, creation_time)" ); - $dbh->do( "CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)" ); + $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); $dbh->do( "CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)" ); $dbh->do( "CREATE INDEX test_results__progress ON test_results (progress)" ); - $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated'))" ); - + $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); + $dbh->commit(); } else { - die "User $params->{username} not authorized to use batch mode\n"; + die Zonemaster::Backend::Error::PermissionDenied->new( message => 'User not authorized to use batch mode', data => { username => $params->{username}} ); } return $batch_id; } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < NOW() - ?::interval - AND nb_retries <= ? AND progress > 0 AND progress < 100 AND queue = ?" ); $sth->execute( # - sprintf( "%d seconds", $self->config->ZONEMASTER_max_zonemaster_execution_time ), - $self->config->ZONEMASTER_maximal_number_of_retries, - $self->config->ZONEMASTER_lock_on_queue, + sprintf( "%d seconds", $test_run_timeout ), + $queue_label, ); return $sth; } else { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < NOW() - ?::interval - AND nb_retries <= ? AND progress > 0 AND progress < 100" ); $sth->execute( # - sprintf( "%d seconds", $self->config->ZONEMASTER_max_zonemaster_execution_time ), - $self->config->ZONEMASTER_maximal_number_of_retries, + sprintf( "%d seconds", $test_run_timeout ), ); return $sth; } @@ -355,10 +343,10 @@ sub process_unfinished_tests_give_up { $self->dbh->do("UPDATE test_results SET progress = 100, test_end_time = NOW(), results = ? WHERE hash_id=?", undef, encode_json($result), $hash_id); } -sub schedule_for_retry { +sub get_relative_start_time { my ( $self, $hash_id ) = @_; - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); + return $self->dbh->selectrow_array("SELECT EXTRACT(EPOCH FROM now() - test_start_time) FROM test_results WHERE hash_id=?", undef, $hash_id); } no Moose; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 433db3da6..0f508f747 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -5,223 +5,134 @@ our $VERSION = '1.1.0'; use Moose; use 5.14.2; -use Data::Dumper; use DBI qw(:utils); use Digest::MD5 qw(md5_hex); -use Encode; use JSON::PP; -use Log::Any qw( $log ); -use Zonemaster::Backend::Config; +use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -has 'config' => ( - is => 'ro', - isa => 'Zonemaster::Backend::Config', - required => 1, -); +=head1 CLASS METHODS -has 'dbh' => ( - is => 'rw', - isa => 'DBI::db', -); +=head2 from_config -sub BUILD { - my ( $self ) = @_; +Construct a new instance from a Zonemaster::Backend::Config. - if ( !defined $self->dbh ) { - my $file = $self->config->SQLITE_database_file; - - $log->notice( "Opening SQLite: file=$file" ) if $log->is_notice; - my $dbh = DBI->connect( - "DBI:SQLite:dbname=$file", - undef, undef, - { - AutoCommit => 1, - RaiseError => 1, - } - ); + my $db = Zonemaster::Backend::DB::SQLite->from_config( $config ); - $self->dbh( $dbh ); - } +=cut + +sub from_config { + my ( $class, $config ) = @_; + + my $file = $config->SQLITE_database_file; - return $self; + my $data_source_name = "DBI:SQLite:dbname=$file"; + + my $dbh = $class->_new_dbh( $data_source_name, '', '' ); + + return $class->new( + { + data_source_name => $data_source_name, + user => '', + password => '', + dbhandle => $dbh, + } + ); } sub DEMOLISH { my ( $self ) = @_; - $self->dbh->disconnect() if $self->dbh; + $self->dbh->disconnect() if defined $self->dbhandle && $self->dbhandle->ping; } sub create_db { my ( $self ) = @_; + my $dbh = $self->dbh; + #################################################################### # TEST RESULTS #################################################################### - $self->dbh->do( 'DROP TABLE IF EXISTS test_specs' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; - - $self->dbh->do( 'DROP TABLE IF EXISTS test_results' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; - + $dbh->do( + 'CREATE TABLE IF NOT EXISTS test_results ( + id integer PRIMARY KEY AUTOINCREMENT, + hash_id VARCHAR(16) NOT NULL, + domain VARCHAR(255) NOT NULL, + batch_id integer NULL, + creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + test_start_time TIMESTAMP DEFAULT NULL, + test_end_time TIMESTAMP DEFAULT NULL, + priority integer DEFAULT 10, + queue integer DEFAULT 0, + progress integer DEFAULT 0, + fingerprint character varying(32), + params text NOT NULL, + results text DEFAULT NULL, + undelegated boolean NOT NULL DEFAULT false + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'test_results' table", data => $dbh->errstr() ); + + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__hash_id ON test_results (hash_id)' + ); $self->dbh->do( - 'CREATE TABLE test_results ( - id integer PRIMARY KEY AUTOINCREMENT, - hash_id VARCHAR(16) DEFAULT NULL, - domain VARCHAR(255) NOT NULL, - batch_id integer NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - test_start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - test_end_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - priority integer DEFAULT 10, - queue integer DEFAULT 0, - progress integer DEFAULT 0, - params_deterministic_hash character varying(32), - params text NOT NULL, - results text DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, - nb_retries integer NOT NULL DEFAULT 0 - ) - ' - ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + 'CREATE INDEX IF NOT EXISTS test_results__fingerprint ON test_results (fingerprint)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__batch_id_progress ON test_results (batch_id, progress)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__progress ON test_results (progress)' + ); + $dbh->do( + 'CREATE INDEX IF NOT EXISTS test_results__domain_undelegated ON test_results (domain, undelegated)' + ); + #################################################################### # BATCH JOBS #################################################################### - $self->dbh->do( 'DROP TABLE IF EXISTS batch_jobs' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + $dbh->do( + 'CREATE TABLE IF NOT EXISTS batch_jobs ( + id integer PRIMARY KEY, + username character varying(50) NOT NULL, + creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'batch_jobs' table", data => $dbh->errstr() ); - $self->dbh->do( - 'CREATE TABLE batch_jobs ( - id integer PRIMARY KEY, - username character varying(50) NOT NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL - ) - ' - ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; #################################################################### # USERS #################################################################### - $self->dbh->do( 'DROP TABLE IF EXISTS users' ); - $self->dbh->do( - 'CREATE TABLE users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username varchar(128), - api_key varchar(512), - user_info json DEFAULT NULL - ) - ' - ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + $dbh->do( + 'CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username varchar(128), + api_key varchar(512) + ) + ' + ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'users' table", data => $dbh->errstr() ); return 1; } -sub user_exists_in_db { - my ( $self, $user ) = @_; - - my ( $id ) = $self->dbh->selectrow_array( "SELECT id FROM users WHERE username = ?", undef, $user ); - - return $id; -} - -sub add_api_user_to_db { - my ( $self, $user_name, $api_key ) = @_; - - my $nb_inserted = $self->dbh->do( - "INSERT INTO users (user_info, username, api_key) VALUES (?,?,?)", - undef, - 'NULL', - $user_name, - $api_key, - ); - - return $nb_inserted; -} - -sub user_authorized { - my ( $self, $user, $api_key ) = @_; - - my ( $id ) = - $self->dbh->selectrow_array( q[SELECT id FROM users WHERE username = ? AND api_key = ?], undef, $user, $api_key ); - - return $id; -} - -sub create_new_batch_job { - my ( $self, $username ) = @_; - - my ( $batch_id, $creaton_time ) = $self->dbh->selectrow_array( " - SELECT - batch_id, - batch_jobs.creation_time AS batch_creation_time - FROM - test_results - JOIN batch_jobs - ON batch_id=batch_jobs.id - AND username=" . $self->dbh->quote( $username ) . " WHERE - test_results.progress<>100 - LIMIT 1 - " ); - - die "You can't create a new batch job, job:[$batch_id] started on:[$creaton_time] still running \n" if ( $batch_id ); - - $self->dbh->do("INSERT INTO batch_jobs (username) VALUES(" . $self->dbh->quote( $username ) . ")" ); - my ( $new_batch_id ) = $self->dbh->sqlite_last_insert_rowid; - - return $new_batch_id; -} - -sub create_new_test { - my ( $self, $domain, $test_params, $seconds, $batch_id ) = @_; +# Search for recent test result with the test same parameters, where +# "age_reuse_previous_test" gives the time limit for how old test result that +# is accepted. +sub recent_test_hash_id { + my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; my $dbh = $self->dbh; - - $test_params->{domain} = $domain; - my $js = JSON::PP->new->canonical; - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( $encoded_params ); - my $result_id; - - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; - - # Search for recent test result with the test same parameters, where "$seconds" - # gives the time limit for how old test result that is accepted. my ( $recent_hash_id ) = $dbh->selectrow_array( - "SELECT hash_id FROM test_results WHERE params_deterministic_hash = ? AND test_start_time > DATETIME('now', ?)", - undef, - $test_params_deterministic_hash, - "-$seconds seconds" + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND test_start_time > DATETIME('now', ?)", + undef, $fingerprint, "-$age_reuse_previous_test seconds" ); - if ( $recent_hash_id ) { - # A recent entry exists, so return its id - $result_id = $recent_hash_id; - } - else { - - # The SQLite database engine does not have support to create the "hash_id" by a - # database engine trigger. "hash_id" is assumed to hold a unique hash. Uniqueness - # cannot, however, be guaranteed. Same as with the other database engines. - my $hash_id = substr(md5_hex(time().rand()), 0, 16); - - my $fields = 'hash_id, batch_id, priority, queue, params_deterministic_hash, params, domain, test_start_time, undelegated'; - $dbh->do( - "INSERT INTO test_results ($fields) VALUES (?,?,?,?,?,?,?, datetime('now'),?)", - undef, - $hash_id, - $batch_id, - $priority, - $queue, - $test_params_deterministic_hash, - $encoded_params, - $test_params->{domain}, - ($test_params->{nameservers})?(1):(0), - ); - $result_id = $hash_id; - } - - return $result_id; # Return test ID, either test previously run or just created. + return $recent_hash_id; } sub test_progress { @@ -242,21 +153,6 @@ sub test_progress { return $result; } -sub get_test_params { - my ( $self, $test_id ) = @_; - - my ( $params_json ) = $self->dbh->selectrow_array( "SELECT params FROM test_results WHERE hash_id=?", undef, $test_id ); - - my $result; - eval { - $result = decode_json( $params_json ); - }; - - $log->warn( "decoding of params_json failed (test_id: [$test_id]):".Dumper($params_json) ) if $@; - - return $result; -} - sub test_results { my ( $self, $test_id, $new_results ) = @_; @@ -268,8 +164,22 @@ sub test_results { my $result; my ( $hrefs ) = $self->dbh->selectall_hashref( "SELECT id, hash_id, creation_time, params, results FROM test_results WHERE hash_id=?", 'hash_id', undef, $test_id ); $result = $hrefs->{$test_id}; - $result->{params} = decode_json( $result->{params} ); - $result->{results} = decode_json( $result->{results} ); + + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $result; + + eval { + $result->{params} = decode_json( $result->{params} ); + + if (defined $result->{results}) { + $result->{results} = decode_json( $result->{results} ); + } else { + $result->{results} = []; + } + }; + + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } @@ -281,9 +191,9 @@ sub get_test_history { my $undelegated = ""; if ($p->{filter} eq "undelegated") { - $undelegated = "AND (params->'nameservers') IS NOT NULL"; + $undelegated = "AND undelegated = 1"; } elsif ($p->{filter} eq "delegated") { - $undelegated = "AND (params->'nameservers') IS NULL"; + $undelegated = "AND undelegated = 0"; } my $quoted_domain = $self->dbh->quote( $p->{frontend_params}->{domain} ); @@ -293,7 +203,8 @@ sub get_test_history { hash_id, creation_time, params, - results + undelegated, + results FROM test_results WHERE @@ -309,7 +220,7 @@ sub get_test_history { my $warning = ( grep { $_->{level} eq 'WARNING' } @{ $h->{results} } ); # More important overwrites - my $overall = 'INFO'; + my $overall = 'ok'; $overall = 'warning' if $warning; $overall = 'error' if $error; $overall = 'critical' if $critical; @@ -317,6 +228,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall, } ); @@ -331,77 +243,74 @@ sub add_batch_job { my $batch_id; my $dbh = $self->dbh; - my $js = JSON::PP->new; - $js->canonical( 1 ); if ( $self->user_authorized( $params->{username}, $params->{api_key} ) ) { $batch_id = $self->create_new_batch_job( $params->{username} ); my $test_params = $params->{test_params}; - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + my $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; $dbh->{AutoCommit} = 0; eval {$dbh->do( "DROP INDEX IF EXISTS test_results__hash_id " );}; - eval {$dbh->do( "DROP INDEX IF EXISTS test_results__params_deterministic_hash " );}; + eval {$dbh->do( "DROP INDEX IF EXISTS test_results__fingerprint " );}; eval {$dbh->do( "DROP INDEX IF EXISTS test_results__batch_id_progress " );}; eval {$dbh->do( "DROP INDEX IF EXISTS test_results__progress " );}; eval {$dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated " );}; - - my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); + + my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, fingerprint, params, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $test_params_deterministic_hash = md5_hex( encode_utf8( $encoded_params ) ); - $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue, $test_params_deterministic_hash, $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); + + my $hash_id = substr(md5_hex(time().rand()), 0, 16); + $sth->execute( $hash_id, $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); } $dbh->do( "CREATE INDEX test_results__hash_id ON test_results (hash_id, creation_time)" ); - $dbh->do( "CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)" ); + $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); $dbh->do( "CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)" ); $dbh->do( "CREATE INDEX test_results__progress ON test_results (progress)" ); $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); - + $dbh->commit(); $dbh->{AutoCommit} = 1; } else { - die "User $params->{username} not authorized to use batch mode\n"; + die Zonemaster::Backend::Error::PermissionDenied->new( message => 'User not authorized to use batch mode', data => { username => $params->{username}} ); } return $batch_id; } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < DATETIME('now', ?) - AND nb_retries <= ? AND progress > 0 AND progress < 100 AND queue = ?" ); $sth->execute( # - sprintf( "-%d seconds", $self->config->ZONEMASTER_max_zonemaster_execution_time ), - $self->config->ZONEMASTER_maximal_number_of_retries, - $self->config->ZONEMASTER_lock_on_queue, + sprintf( "-%d seconds", $test_run_timeout ), + $queue_label, ); return $sth; } else { my $sth = $self->dbh->prepare( " - SELECT hash_id, results, nb_retries + SELECT hash_id, results FROM test_results WHERE test_start_time < DATETIME('now', ?) - AND nb_retries <= ? AND progress > 0 AND progress < 100" ); $sth->execute( # - sprintf( "-%d seconds", $self->config->ZONEMASTER_max_zonemaster_execution_time ), - $self->config->ZONEMASTER_maximal_number_of_retries, + sprintf( "-%d seconds", $test_run_timeout ), ); return $sth; } @@ -413,10 +322,10 @@ sub process_unfinished_tests_give_up { $self->dbh->do("UPDATE test_results SET progress = 100, test_end_time = DATETIME('now'), results = ? WHERE hash_id=?", undef, encode_json($result), $hash_id); } -sub schedule_for_retry { +sub get_relative_start_time { my ( $self, $hash_id ) = @_; - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = DATETIME('now') WHERE hash_id=?", undef, $hash_id); + return $self->dbh->selectrow_array("SELECT (julianday('now') - julianday(test_start_time)) * 3600 * 24 FROM test_results WHERE hash_id=?", undef, $hash_id); } no Moose; diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm new file mode 100644 index 000000000..cc25cf877 --- /dev/null +++ b/lib/Zonemaster/Backend/Errors.pm @@ -0,0 +1,157 @@ +package Zonemaster::Backend::Error; +use Moose; +use Data::Dumper; + + +has 'message' => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'code' => ( + is => 'ro', + isa => 'Int', + required => 1, +); + +has 'data' => ( + is => 'ro', + isa => 'Any', + default => undef, +); + +sub as_hash { + my $self = shift; + my $error = { + code => $self->code, + message => $self->message, + error => ref($self), + }; + $error->{data} = $self->data if defined $self->data; + return $error; +} + +sub as_string { + my $self = shift; + my $str = sprintf "%s (code %d).", $self->message, $self->code; + if (defined $self->data) { + $str .= sprintf " Context: %s", $self->_data_dump; + } + return $str; +} + +sub _data_dump { + my $self = shift; + local $Data::Dumper::Indent = 0; + local $Data::Dumper::Terse = 1; + my $data = Dumper($self->data); + $data =~ s/[\n\r]/ /g; + return $data ; +} + +package Zonemaster::Backend::Error::Internal; +use Moose; + +extends 'Zonemaster::Backend::Error'; + +has '+message' => ( + default => 'Internal server error' +); + +has '+code' => ( + default => -32603 +); + +has 'reason' => ( + isa => 'Str', + is => 'ro' +); + +has 'method' => ( + is => 'ro', + isa => 'Str', + builder => '_build_method' +); + +sub _build_method { + my $s = 0; + while (my @c = caller($s)) { + $s ++; + last if $c[3] eq 'Moose::Object::new'; + } + my @c = caller($s); + if ($c[3] =~ /^(.*)::handle_exception$/ ) { + @c = caller(++$s); + } + + return $c[3]; +} + +around 'BUILDARGS', sub { + my ($orig, $class, %args) = @_; + + if(exists $args{reason}) { + # trim new lines + $args{reason} =~ s/\n/ /g; + $args{reason} =~ s/^\s+|\s+$//g; + } + + $class->$orig(%args); +}; + +sub as_string { + my $self = shift; + my $str = sprintf "Caught %s in the `%s` method: %s", ref($self), $self->method, $self->reason; + if (defined $self->data) { + $str .= sprintf " Context: %s", $self->_data_dump; + } + return $str; +} + + +package Zonemaster::Backend::Error::ResourceNotFound; +use Moose; + +extends 'Zonemaster::Backend::Error'; + +has '+message' => ( + default => 'Resource not found' +); + +has '+code' => ( + default => -32000 +); + +package Zonemaster::Backend::Error::PermissionDenied; +use Moose; + +extends 'Zonemaster::Backend::Error'; + +has '+message' => ( + default => 'Permission denied' +); + +has '+code' => ( + default => -32001 +); + +package Zonemaster::Backend::Error::Conflict; +use Moose; + +extends 'Zonemaster::Backend::Error'; + +has '+message' => ( + default => 'Conflicting resource' +); + +has '+code' => ( + default => -32002 +); + +package Zonemaster::Backend::Error::JsonError; +use Moose; + +extends 'Zonemaster::Backend::Error::Internal'; + +1; diff --git a/lib/Zonemaster/Backend/Metrics.pm b/lib/Zonemaster/Backend/Metrics.pm new file mode 100644 index 000000000..e2b40310e --- /dev/null +++ b/lib/Zonemaster/Backend/Metrics.pm @@ -0,0 +1,57 @@ +package Zonemaster::Backend::Metrics; + +use Log::Any qw($log); + +eval("use Net::Statsd"); + +my $enable_metrics = 0; + +if (!$@) { + $enable_metrics = 1; +} + +my %CODE_STATUS_HASH = ( + -32700 => 'RPC_PARSE_ERROR', + -32600 => 'RPC_INVALID_REQUEST', + -32601 => 'RPC_METHOD_NOT_FOUND', + -32602 => 'RPC_INVALID_PARAMS', + -32603 => 'RPC_INTERNAL_ERROR' +); + +sub setup { + my ( $cls, $host, $port ) = @_; + if (!defined $host) { + $enable_metrics = 0; + } elsif ( $enable_metrics ) { + $log->info('Enabling metrics module', { host => $host, port => $port }); + $Net::Statsd::HOST = $host; + $Net::Statsd::PORT = $port; + } +} + +sub code_to_status { + my ($cls, $code) = @_; + if (defined $code) { + return %CODE_STATUS_HASH{$code}; + } else { + return 'RPC_SUCCESS' + } +} + +sub increment { + if ( $enable_metrics ) { + Net::Statsd::increment(@_) + } +} + +sub gauge { + if ( $enable_metrics ) { + Net::Statsd::gauge(@_) + } +} + +sub timing { + if ( $enable_metrics ) { + Net::Statsd::timing(@_) + } +} diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 7ef40ca9c..f1be2fb97 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -5,13 +5,21 @@ use warnings; use 5.14.2; # Public Modules -use JSON::PP; use DBI qw(:utils); use Digest::MD5 qw(md5_hex); -use String::ShellQuote; use File::Slurp qw(append_file); use HTML::Entities; +use JSON::PP; use JSON::Validator::Joi; +use Log::Any qw($log); +use String::ShellQuote; +use Mojo::JSON::Pointer; +use Scalar::Util qw(blessed); +use JSON::Validator::Schema::Draft7; +use Locale::TextDomain qw[Zonemaster-Backend]; +use Locale::Messages qw[LC_MESSAGES LC_ALL]; +use POSIX qw (setlocale); +use Encode; # Zonemaster Modules use Zonemaster::LDNS; @@ -25,9 +33,11 @@ use Zonemaster::Backend; use Zonemaster::Backend::Config; use Zonemaster::Backend::Translator; use Zonemaster::Backend::Validator; +use Zonemaster::Backend::Errors; my $zm_validator = Zonemaster::Backend::Validator->new; -my %json_schemas; +our %json_schemas; +our %extra_validators; my $recursor = Zonemaster::Engine::Recursor->new; sub joi { @@ -41,7 +51,7 @@ sub new { bless( $self, $type ); if ( ! $params || ! $params->{config} ) { - handle_exception('new', "Missing 'config' parameter", '001'); + handle_exception("Missing 'config' parameter"); } $self->{config} = $params->{config}; @@ -62,23 +72,30 @@ sub _init_db { my ( $self, $dbtype ) = @_; eval { - my $backend_module = "Zonemaster::Backend::DB::" . $dbtype; - eval "require $backend_module"; - die "$@ \n" if $@; - $self->{db} = $backend_module->new( { config => $self->{config} } ); + my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); + $self->{db} = $dbclass->from_config( $self->{config} ); }; + if ($@) { - handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); + handle_exception("Failed to initialize the [$dbtype] database backend module: [$@]"); } } sub handle_exception { - my ( $method, $exception, $exception_id ) = @_; + my ( $exception ) = @_; + + if ( !$exception->isa('Zonemaster::Backend::Error') ) { + my $reason = $exception; + $exception = Zonemaster::Backend::Error::Internal->new( reason => $reason ); + } + + if ( $exception->isa('Zonemaster::Backend::Error::Internal') ) { + $log->error($exception->as_string); + } else { + $log->info($exception->as_string); + } - $exception =~ s/\n/ /g; - $exception =~ s/^\s+|\s+$//g; - warn "Internal error $exception_id: Unexpected error in the $method API call: [$exception] \n"; - die "Internal error $exception_id \n"; + die $exception->as_hash; } $json_schemas{version_info} = joi->object->strict; @@ -92,7 +109,7 @@ sub version_info { }; if ($@) { - handle_exception('version_info', $@, '003'); + handle_exception( $@ ); } return \%ver; @@ -102,16 +119,13 @@ $json_schemas{profile_names} = joi->object->strict; sub profile_names { my ( $self ) = @_; - my @profiles; - eval { - @profiles = $self->{config}->ListPublicProfiles(); - - }; - if ($@) { - handle_exception('profile_names', $@, '004'); + my %profiles; + eval { %profiles = $self->{config}->PUBLIC_PROFILES }; + if ( $@ ) { + handle_exception( $@ ); } - return \@profiles; + return [ keys %profiles ]; } # Return the list of language tags supported by get_test_results(). The tags are @@ -120,14 +134,33 @@ $json_schemas{get_language_tags} = joi->object->strict; sub get_language_tags { my ( $self ) = @_; - my @lang = $self->{config}->ListLanguageTags(); + my @lang_tags; + eval { + my %locales = $self->{config}->LANGUAGE_locale; - return \@lang; + for my $lang ( sort keys %locales ) { + my @locale_tags = sort keys %{ $locales{$lang} }; + if ( scalar @locale_tags == 1 ) { + push @lang_tags, $lang; + } + push @lang_tags, @locale_tags; + } + }; + if ( $@ ) { + handle_exception( $@ ); + } + + return \@lang_tags; } -$json_schemas{get_host_by_name} = joi->object->strict->props( - hostname => $zm_validator->domain_name->required -); +$json_schemas{get_host_by_name} = { + type => 'object', + additionalProperties => 0, + required => [ 'hostname' ], + properties => { + hostname => $zm_validator->domain_name + } +}; sub get_host_by_name { my ( $self, $params ) = @_; my @adresses; @@ -140,26 +173,38 @@ sub get_host_by_name { }; if ($@) { - handle_exception('get_host_by_name', $@, '005'); + handle_exception( $@ ); } return \@adresses; } -$json_schemas{get_data_from_parent_zone} = joi->object->strict->props( - domain => $zm_validator->domain_name->required -); +$extra_validators{get_data_from_parent_zone} = sub { + my ( $self, $syntax_input ) = @_; + my @errors; + + if ( defined $syntax_input->{domain} ) { + my ( undef, $dn_syntax ) = $self->_check_domain( $syntax_input->{domain} ); + push @errors, { path => "/domain", message => $dn_syntax->{message} } if ( $dn_syntax->{status} eq 'nok' ); + } + return @errors; +}; +$json_schemas{get_data_from_parent_zone} = { + type => 'object', + additionalProperties => 0, + required => [ 'domain' ], + properties => { + domain => $zm_validator->domain_name, + language => $zm_validator->language_tag, + } +}; sub get_data_from_parent_zone { my ( $self, $params ) = @_; my $result = eval { my %result; - my $domain = $params->{domain}; - my ( $dn, $dn_syntax ) = $self->_check_domain( $domain, 'Domain name' ); - return $dn_syntax if ( $dn_syntax->{status} eq 'nok' ); - my @ns_list; my @ns_names; @@ -184,7 +229,7 @@ sub get_data_from_parent_zone { return \%result; }; if ($@) { - handle_exception('get_data_from_parent_zone', $@, '006'); + handle_exception( $@ ); } elsif ($result) { return $result; @@ -193,10 +238,10 @@ sub get_data_from_parent_zone { sub _check_domain { - my ( $self, $domain, $type ) = @_; + my ( $self, $domain ) = @_; if ( !defined( $domain ) ) { - return ( $domain, { status => 'nok', message => encode_entities( "$type required" ) } ); + return ( $domain, { status => 'nok', message => N__ 'Domain name required' } ); } if ( $domain =~ m/[^[:ascii:]]+/ ) { @@ -207,7 +252,7 @@ sub _check_domain { $domain, { status => 'nok', - message => encode_entities( "The domain name is not a valid IDNA string and cannot be converted to an A-label" ) + message => N__ 'The domain name is not a valid IDNA string and cannot be converted to an A-label' } ); } @@ -217,7 +262,7 @@ sub _check_domain { $domain, { status => 'nok', - message => encode_entities( "$type contains non-ascii characters and IDNA is not installed" ) + message => N__ 'The domain name contains non-ascii characters and IDNA is not installed' } ); } @@ -228,7 +273,7 @@ sub _check_domain { $domain, { status => 'nok', - message => encode_entities( "The domain name character(s) not supported" ) + message => N__ 'The domain name character(s) are not supported' } ); } @@ -238,158 +283,95 @@ sub _check_domain { @res = Zonemaster::Engine::Test::Basic->basic00( $domain ); @res = grep { $_->numeric_level >= $levels{ERROR} } @res; if ( @res != 0 ) { - return ( $domain, { status => 'nok', message => encode_entities( "$type name or label is too long" ) } ); + return ( $domain, { status => 'nok', message => N__ 'The domain name or label is too long' } ); } return ( $domain, { status => 'ok', message => 'Syntax ok' } ); } -sub validate_syntax { +$extra_validators{start_domain_test} = sub { my ( $self, $syntax_input ) = @_; - my $result = eval { - my @allowed_params_keys = ( - 'domain', 'ipv4', 'ipv6', 'ds_info', 'nameservers', 'profile', - 'client_id', 'client_version', 'config', 'priority', 'queue' - ); - - foreach my $k ( keys %$syntax_input ) { - return { status => 'nok', message => encode_entities( "Unknown option [$k] in parameters" ) } - unless ( grep { $_ eq $k } @allowed_params_keys ); - } - - if ( ( defined $syntax_input->{nameservers} && @{ $syntax_input->{nameservers} } ) ) { - foreach my $ns_ip ( @{ $syntax_input->{nameservers} } ) { - foreach my $k ( keys %$ns_ip ) { - delete( $ns_ip->{$k} ) unless ( $k eq 'ns' || $k eq 'ip' ); - } - } - } + my @errors = eval { + my @errors; - if ( ( defined $syntax_input->{ds_info} && @{ $syntax_input->{ds_info} } ) ) { - foreach my $ds_digest ( @{ $syntax_input->{ds_info} } ) { - foreach my $k ( keys %$ds_digest ) { - delete( $ds_digest->{$k} ) unless ( $k eq 'algorithm' || $k eq 'digest' || $k eq 'digtype' || $k eq 'keytag' ); - } + if ( defined $syntax_input->{profile} ) { + $syntax_input->{profile} = lc $syntax_input->{profile}; + my %profiles = ( $self->{config}->PUBLIC_PROFILES, $self->{config}->PRIVATE_PROFILES ); + if ( !exists $profiles{ $syntax_input->{profile} } ) { + push @errors, { path => '/profile', message => N__ 'Unknown profile' }; } } - if ( defined $syntax_input->{ipv4} ) { - return { status => 'nok', message => encode_entities( "Invalid IPv4 transport option format" ) } - unless ( $syntax_input->{ipv4} eq JSON::PP::false - || $syntax_input->{ipv4} eq JSON::PP::true - || $syntax_input->{ipv4} eq '1' - || $syntax_input->{ipv4} eq '0' ); + if ( defined $syntax_input->{domain} ) { + my ( undef, $dn_syntax ) = $self->_check_domain( $syntax_input->{domain} ); + push @errors, { path => "/domain", message => $dn_syntax->{message} } if ( $dn_syntax->{status} eq 'nok' ); } - if ( defined $syntax_input->{ipv6} ) { - return { status => 'nok', message => encode_entities( "Invalid IPv6 transport option format" ) } - unless ( $syntax_input->{ipv6} eq JSON::PP::false - || $syntax_input->{ipv6} eq JSON::PP::true - || $syntax_input->{ipv6} eq '1' - || $syntax_input->{ipv6} eq '0' ); - } - - if ( defined $syntax_input->{profile} ) { - my @profiles = map lc, $self->{config}->ListPublicProfiles(); - return { status => 'nok', message => encode_entities( "Invalid profile option format" ) } - unless ( grep { $_ eq lc $syntax_input->{profile} } @profiles ); - } - - my ( undef, $dn_syntax ) = $self->_check_domain( $syntax_input->{domain}, 'Domain name' ); - - return $dn_syntax if ( $dn_syntax->{status} eq 'nok' ); - - if ( defined $syntax_input->{nameservers} && @{ $syntax_input->{nameservers} } ) { - foreach my $ns_ip ( @{ $syntax_input->{nameservers} } ) { - my ( $ns, $ns_syntax ) = $self->_check_domain( $ns_ip->{ns}, "NS [$ns_ip->{ns}]" ); - return $ns_syntax if ( $ns_syntax->{status} eq 'nok' ); - } - - foreach my $ns_ip ( @{ $syntax_input->{nameservers} } ) { - # Although counterintuitive both tests are necessary as Zonemaster::Engine::Net::IP accepts incomplete IP adresses (network adresses) as valid IP adresses - return { status => 'nok', message => encode_entities( "Invalid IP address: [$ns_ip->{ip}]" ) } - unless( !$ns_ip->{ip} || $ns_ip->{ip} =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ || $ns_ip->{ip} =~ /^([0-9A-Fa-f]{1,4}:[0-9A-Fa-f:]{1,}(:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?)|([0-9A-Fa-f]{1,4}::[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/); + if ( defined $syntax_input->{nameservers} && ref $syntax_input->{nameservers} eq 'ARRAY' && @{ $syntax_input->{nameservers} } ) { + while (my ($index, $ns_ip) = each @{ $syntax_input->{nameservers} }) { + my ( $ns, $ns_syntax ) = $self->_check_domain( $ns_ip->{ns} ); + push @errors, { path => "/nameservers/$index/ns", message => $ns_syntax->{message} } if ( $ns_syntax->{status} eq 'nok' ); - return { status => 'nok', message => encode_entities( "Invalid IP address: [$ns_ip->{ip}]" ) } + push @errors, { path => "/nameservers/$index/ip", message => N__ 'Invalid IP address' } unless ( !$ns_ip->{ip} || Zonemaster::Engine::Net::IP::ip_is_ipv4( $ns_ip->{ip} ) || Zonemaster::Engine::Net::IP::ip_is_ipv6( $ns_ip->{ip} ) ); } - - foreach my $ds_digest ( @{ $syntax_input->{ds_info} } ) { - return { - status => 'nok', - message => encode_entities( "Invalid algorithm type: [$ds_digest->{algorithm}]" ) - } - if ( $ds_digest->{algorithm} =~ /\D/ ); - } - - foreach my $ds_digest ( @{ $syntax_input->{ds_info} } ) { - return { - status => 'nok', - message => encode_entities( "Invalid digest format: [$ds_digest->{digest}]" ) - } - if ( - ( length( $ds_digest->{digest} ) != 96 && - length( $ds_digest->{digest} ) != 64 && - length( $ds_digest->{digest} ) != 40 ) || - $ds_digest->{digest} =~ /[^A-Fa-f0-9]/ - ); - } } + + return @errors; }; if ($@) { - handle_exception('validate_syntax', $@, '008'); - } - elsif ($result) { - return $result; + handle_exception( $@ ); } else { - return { status => 'ok', message => encode_entities( 'Syntax ok' ) }; + return @errors; } -} +}; + +$json_schemas{start_domain_test} = { + type => 'object', + additionalProperties => 0, + required => [ 'domain' ], + properties => { + domain => $zm_validator->domain_name, + ipv4 => joi->boolean->compile, + ipv6 => joi->boolean->compile, + nameservers => { + type => 'array', + items => $zm_validator->nameserver + }, + ds_info => { + type => 'array', + items => $zm_validator->ds_info + }, + profile => $zm_validator->profile_name->compile, + client_id => $zm_validator->client_id->compile, + client_version => $zm_validator->client_version->compile, + config => joi->string->compile, + priority => $zm_validator->priority->compile, + queue => $zm_validator->queue->compile, + language => $zm_validator->language_tag, -$json_schemas{start_domain_test} = joi->object->strict->props( - domain => $zm_validator->domain_name->required, - ipv4 => joi->boolean, - ipv6 => joi->boolean, - nameservers => joi->array->items( - $zm_validator->nameserver - ), - ds_info => joi->array->items( - $zm_validator->ds_info - ), - profile => $zm_validator->profile_name, - client_id => $zm_validator->client_id, - client_version => $zm_validator->client_version, - config => joi->string, - priority => $zm_validator->priority, - queue => $zm_validator->queue -); + } +}; sub start_domain_test { my ( $self, $params ) = @_; my $result = 0; eval { $params->{domain} =~ s/^\.// unless ( !$params->{domain} || $params->{domain} eq '.' ); - my $syntax_result = $self->validate_syntax( $params ); - die "$syntax_result->{message} \n" unless ( $syntax_result && $syntax_result->{status} eq 'ok' ); die "No domain in parameters\n" unless ( $params->{domain} ); - if ($params->{config}) { - $params->{config} =~ s/[^\w_]//isg; - die "Unknown test configuration: [$params->{config}]\n" unless ( $self->{config}->GetCustomConfigParameter('ZONEMASTER', $params->{config}) ); - } - $params->{priority} //= 10; $params->{queue} //= 0; $result = $self->{db}->create_new_test( $params->{domain}, $params, $self->{config}->ZONEMASTER_age_reuse_previous_test ); }; if ($@) { - handle_exception('start_domain_test', $@, '009'); + handle_exception( $@ ); } return $result; @@ -407,7 +389,7 @@ sub test_progress { $result = $self->{db}->test_progress( $test_id ); }; if ($@) { - handle_exception('test_progress', $@, '010'); + handle_exception( $@ ); } return $result; @@ -419,50 +401,49 @@ $json_schemas{get_test_params} = joi->object->strict->props( sub get_test_params { my ( $self, $params ) = @_; - my $test_id = $params->{test_id}; - - my $result = 0; - + my $result; eval { + my $test_id = $params->{test_id}; + $result = $self->{db}->get_test_params( $test_id ); }; if ($@) { - handle_exception('get_test_params', $@, '011'); + handle_exception( $@ ); } return $result; } -$json_schemas{get_test_results} = joi->object->strict->props( - id => $zm_validator->test_id->required, - language => $zm_validator->language_tag->required -); +$json_schemas{get_test_results} = { + type => 'object', + additionalProperties => 0, + required => [ 'id', 'language' ], + properties => { + id => $zm_validator->test_id->required->compile, + language => $zm_validator->language_tag, + } +}; sub get_test_results { my ( $self, $params ) = @_; my $result; - my $translator; - $translator = Zonemaster::Backend::Translator->new; + eval{ + # Already validated by json_validate + my ( $locale, undef ) = $self->_get_locale( $params ); + + my $translator; + $translator = Zonemaster::Backend::Translator->new; - my %locale = $self->{config}->Language_Locale_hash(); - if ( $locale{$params->{language}} ) { - if ( $locale{$params->{language}} eq 'NOT-UNIQUE') { - die "Language string not unique: '$params->{language}'\n"; + my $previous_locale = $translator->locale; + if ( !$translator->locale( $locale ) ) { + die "Failed to set locale: $locale"; } - } - else { - die "Undefined language string: '$params->{language}'\n"; - } - my $previous_locale = $translator->locale; - $translator->locale( $locale{$params->{language}} ); + eval { $translator->data } if $translator; # Provoke lazy loading of translation data - eval { $translator->data } if $translator; # Provoke lazy loading of translation data + my @zm_results; - my $test_info; - my @zm_results; - eval{ - $test_info = $self->{db}->test_results( $params->{id} ); + my $test_info = $self->{db}->test_results( $params->{id} ); foreach my $test_res ( @{ $test_info->{results} } ) { my $res; if ( $test_res->{module} eq 'NAMESERVER' ) { @@ -476,7 +457,7 @@ sub get_test_results { } $res->{module} = $test_res->{module}; - $res->{message} = $translator->translate_tag( $test_res, $params->{language} ) . "\n"; + $res->{message} = $translator->translate_tag( $test_res ) . "\n"; $res->{message} =~ s/,/, /isg; $res->{message} =~ s/;/; /isg; $res->{level} = $test_res->{level}; @@ -511,27 +492,37 @@ sub get_test_results { $result = $test_info; $result->{results} = \@zm_results; + + $translator->locale( $previous_locale ); + + $result = $test_info; + $result->{results} = \@zm_results; }; if ($@) { - handle_exception('get_test_results', $@, '012'); + handle_exception( $@ ); } - $translator->locale( $previous_locale ); - - $result = $test_info; - $result->{results} = \@zm_results; - return $result; } -$json_schemas{get_test_history} = joi->object->strict->props( - offset => joi->integer->min(0), - limit => joi->integer->min(0), - filter => joi->string->regex('^(?:all|delegated|undelegated)$'), - frontend_params => joi->object->strict->props( - domain => $zm_validator->domain_name->required - )->required -); +$json_schemas{get_test_history} = { + type => 'object', + additionalProperties => 0, + required => [ 'frontend_params' ], + properties => { + offset => joi->integer->min(0)->compile, + limit => joi->integer->min(0)->compile, + filter => joi->string->regex('^(?:all|delegated|undelegated)$')->compile, + frontend_params => { + type => 'object', + additionalProperties => 0, + required => [ 'domain' ], + properties => { + domain => $zm_validator->domain_name + } + } + } +}; sub get_test_history { my ( $self, $params ) = @_; @@ -543,9 +534,12 @@ sub get_test_history { $params->{filter} //= "all"; $results = $self->{db}->get_test_history( $params ); + my @results = map { { %$_, undelegated => $_->{undelegated} ? JSON::PP::true : JSON::PP::false } } @$results; + $results = \@results; + }; if ($@) { - handle_exception('get_test_history', $@, '013'); + handle_exception( $@ ); } return $results; @@ -563,7 +557,7 @@ sub add_api_user { eval { my $allow = 0; if ( defined $remote_ip ) { - $allow = 1 if ( $remote_ip eq '::1' || $remote_ip eq '127.0.0.1' ); + $allow = 1 if ( $remote_ip eq '::1' || $remote_ip eq '127.0.0.1' || $remote_ip eq '::ffff:127.0.0.1' ); } else { $allow = 1; @@ -572,49 +566,62 @@ sub add_api_user { if ( $allow ) { $result = 1 if ( $self->{db}->add_api_user( $params->{username}, $params->{api_key} ) eq '1' ); } + else { + die Zonemaster::Backend::Error::PermissionDenied->new( + message => 'Call to "add_api_user" method not permitted from a remote IP', + data => { remote_ip => $remote_ip } + ); + } }; if ($@) { - handle_exception('add_api_user', $@, '014'); + handle_exception( $@ ); } return $result; } -$json_schemas{add_batch_job} = joi->object->strict->props( - username => $zm_validator->username->required, - api_key => $zm_validator->api_key->required, - domains => joi->array->strict->items( - $zm_validator->domain_name->required - )->required, - test_params => joi->object->strict->props( - ipv4 => joi->boolean, - ipv6 => joi->boolean, - nameservers => joi->array->strict->items( - $zm_validator->nameserver - ), - ds_info => joi->array->strict->items( - $zm_validator->ds_info - ), - profile => $zm_validator->profile_name, - client_id => $zm_validator->client_id, - client_version => $zm_validator->client_version, - config => joi->string, - priority => $zm_validator->priority, - queue => $zm_validator->queue - ) -); +$json_schemas{add_batch_job} = { + type => 'object', + additionalProperties => 0, + required => [ 'username', 'api_key', 'domains' ], + properties => { + username => $zm_validator->username->required->compile, + api_key => $zm_validator->api_key->required->compile, + domains => { + type => "array", + additionalItems => 0, + items => $zm_validator->domain_name + }, + test_params => joi->object->strict->props( + ipv4 => joi->boolean, + ipv6 => joi->boolean, + nameservers => joi->array->strict->items( + $zm_validator->nameserver + ), + ds_info => joi->array->strict->items( + $zm_validator->ds_info + ), + profile => $zm_validator->profile_name, + client_id => $zm_validator->client_id, + client_version => $zm_validator->client_version, + config => joi->string, + priority => $zm_validator->priority, + queue => $zm_validator->queue + )->compile + } +}; sub add_batch_job { my ( $self, $params ) = @_; - $params->{test_params}->{priority} //= 5; - $params->{test_params}->{queue} //= 0; - my $results; eval { + $params->{test_params}->{priority} //= 5; + $params->{test_params}->{queue} //= 0; + $results = $self->{db}->add_batch_job( $params ); }; if ($@) { - handle_exception('add_batch_job', $@, '015'); + handle_exception( $@ ); } return $results; @@ -627,19 +634,81 @@ sub get_batch_job_result { my ( $self, $params ) = @_; my $result; - eval { my $batch_id = $params->{batch_id}; $result = $self->{db}->get_batch_job_result($batch_id); }; if ($@) { - handle_exception('get_batch_job_result', $@, '016'); + handle_exception( $@ ); } return $result; } +sub _get_locale { + my ( $self, $params ) = @_; + my @error; + + my $language = $params->{language}; + my %locales = $self->{config}->LANGUAGE_locale; + my $locale; + + if ( !defined $language ) { + return $locale, \@error; + } + + if ( length $language == 2 ) { + if ( !exists $locales{$language} ) { + push @error, { + path => "/language", + message => N__ "Unkown language string" + }; + } + elsif ( scalar keys %{ $locales{$language} } > 1 ) { + push @error, { + path => "/language", + message => N__ "Language string not unique" + }; + } else { + ( $locale ) = keys %{ $locales{$language} }; + } + } + else { + if ( !exists $locales{substr $language, 0, 2}{$language} ) { + push @error, { + path => "/language", + message => N__ "Unkown language string" + }; + } else { + $locale = $language; + } + } + + if (defined $locale) { + $locale .= '.UTF-8'; + } + + return $locale, \@error, +} + +sub _set_error_message_locale { + my ( $self, $params ) = @_; + + my @error_response = (); + my ($locale, $locale_error) = $self->_get_locale( $params ); + push @error_response, @{$locale_error}; + + if (not defined $locale or $locale eq "") { + # Don't translate message if locale is not defined + $locale = "C"; + } + + # Use POSIX implementation instead of Locale::Messages wrapper + setlocale( LC_ALL, $locale ); + return @error_response; +} + my $rpc_request = joi->object->props( jsonrpc => joi->string->required, method => $zm_validator->jsonrpc_method()->required); @@ -648,6 +717,7 @@ sub jsonrpc_validate { my @error_rpc = $rpc_request->validate($jsonrpc_request); if (!exists $jsonrpc_request->{id} || @error_rpc) { + $self->_set_error_message_locale; return { jsonrpc => '2.0', id => undef, @@ -672,15 +742,17 @@ sub jsonrpc_validate { } }; } - my @error = $method_schema->validate($jsonrpc_request->{params}); - if ( @error ) { + my $sub_validator = $extra_validators{$jsonrpc_request->{method}}; + my @error_response = $self->validate_params($method_schema, $sub_validator, $jsonrpc_request->{params}); + + if ( scalar @error_response ) { return { jsonrpc => '2.0', id => $jsonrpc_request->{id}, error => { code => '-32602', - message => 'Invalid method parameter(s).', - data => "@error" + message => decode_utf8(__ 'Invalid method parameter(s).'), + data => \@error_response } }; } @@ -688,4 +760,62 @@ sub jsonrpc_validate { return ''; } + +sub validate_params { + my ( $self, $method_schema, $sub_validator, $params ) = @_; + my @error_response = (); + + push @error_response, $self->_set_error_message_locale( $params ); + + if (blessed $method_schema) { + $method_schema = $method_schema->compile; + } + my @json_validation_error = JSON::Validator::Schema::Draft7->new->coerce('booleans,numbers,strings')->data($method_schema)->validate( $params ); + + # Customize error message from json validation + foreach my $err ( @json_validation_error ) { + my $message = $err->message; + my @details = @{$err->details}; + + # Handle 'required' errors globally so it does not get overwritten + if ($details[1] eq 'required') { + $message = decode_utf8(__ 'Missing property'); + } else { + my @path = split '/', $err->path, -1; + shift @path; # first item is an empty string + my $found = 1; + my $data = Mojo::JSON::Pointer->new($method_schema); + + foreach my $p (@path) { + if ( $data->contains("/properties/$p") ) { + $data = $data->get("/properties/$p") + } elsif ( $p =~ /^\d+$/ and $data->contains("/items") ) { + $data = $data->get("/items") + } else { + $found = 0; + last; + } + $data = Mojo::JSON::Pointer->new($data); + } + + if ($found and exists $data->data->{'x-error-message'}) { + $message = $data->data->{'x-error-message'}; + } + } + + push @error_response, { path => $err->path, message => $message }; + + } + + # Add messages from extra validation function + if ( defined $sub_validator ) { + push @error_response, $self->$sub_validator($params); + } + + # Translate messages + @error_response = map { { %$_, ( message => decode_utf8 __ $_->{message} ) } } @error_response; + + return @error_response; +} + 1; diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index d07f04062..b08e100f6 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -21,34 +21,36 @@ sub new { my ( $class, $params ) = @_; my $self = {}; - if ( ! $params || ! $params->{config} ) { + if ( !$params || !$params->{config} ) { die "missing 'config' parameter"; } - $self->{config} = $params->{config}; + my $config = $params->{config}; my $dbtype; if ( $params->{dbtype} ) { - $dbtype = $self->{config}->check_db($params->{dbtype}); + $dbtype = $config->check_db( $params->{dbtype} ); } else { - $dbtype = $self->{config}->DB_engine; + $dbtype = $config->DB_engine; } - my $backend_module = "Zonemaster::Backend::DB::" . $dbtype; - eval "require $backend_module"; - $self->{db} = $backend_module->new( { config => $self->{config} } ); + my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); + $self->{_db} = $dbclass->from_config( $config ); - $self->{profiles} = $self->{config}->ReadProfilesInfo(); - foreach my $profile (keys %{$self->{profiles}}) { - die "default profile cannot be private" if ($profile eq 'default' && $self->{profiles}->{$profile}->{type} eq 'private'); - if ( -e $self->{profiles}->{$profile}->{profile_file_name} ) { - my $json = read_file( $self->{profiles}->{$profile}->{profile_file_name}, err_mode => 'croak' ); - $self->{profiles}->{$profile}->{zm_profile} = Zonemaster::Engine::Profile->from_json( $json ); - } - elsif ($profile ne 'default') { - die "the profile definition json file of the profile [$profile] defined in the backend config file can't be read"; + my %all_profiles = ( $config->PUBLIC_PROFILES, $config->PRIVATE_PROFILES ); + foreach my $name ( keys %all_profiles ) { + my $path = $all_profiles{$name}; + + my $full_profile = Zonemaster::Engine::Profile->default; + if ( defined $path ) { + my $json = eval { read_file( $path, err_mode => 'croak' ) } # + // die "Error loading profile '$name': $@"; + my $named_profile = eval { Zonemaster::Engine::Profile->from_json( $json ) } # + // die "Error loading profile '$name' at '$path': $@"; + $full_profile->merge( $named_profile ); } + $self->{_profiles}{$name} = $full_profile; } bless( $self, $class ); @@ -63,9 +65,9 @@ sub run { my $params; - my $progress = $self->{db}->test_progress( $test_id, 1 ); + my $progress = $self->{_db}->test_progress( $test_id, 1 ); - $params = $self->{db}->get_test_params( $test_id ); + $params = $self->{_db}->get_test_params( $test_id ); my %methods = Zonemaster::Engine->all_methods; @@ -114,7 +116,7 @@ sub run { scalar( keys %{ $counter_for_progress_indicator{planned} } ) ) ); - $self->{db}->test_progress( $test_id, $percent_progress ); + $self->{_db}->test_progress( $test_id, $percent_progress ); $previous_method = $module_method; } @@ -138,13 +140,11 @@ sub run { # If the profile parameter has been set in the API, then load a profile if ( $params->{profile} ) { $params->{profile} = lc($params->{profile}); - if (defined $self->{profiles}->{$params->{profile}} && $self->{profiles}->{$params->{profile}}->{zm_profile}) { - my $profile = Zonemaster::Engine::Profile->default; - $profile->merge( $self->{profiles}->{$params->{profile}}->{zm_profile} ); - Zonemaster::Engine::Profile->effective->merge( $profile ); + if ( defined $self->{_profiles}{ $params->{profile} } ) { + Zonemaster::Engine::Profile->effective->merge( $self->{_profiles}{ $params->{profile} } ); } else { - die "The profile [$params->{profile}] is not defined in the backend_config ini file" if ($params->{profile} ne 'default') + die "The profile [$params->{profile}] is not defined in the backend_config ini file"; } } @@ -170,9 +170,9 @@ sub run { } } - $self->{db}->test_results( $test_id, Zonemaster::Engine->logger->json( 'INFO' ) ); + $self->{_db}->test_results( $test_id, Zonemaster::Engine->logger->json( 'INFO' ) ); - $progress = $self->{db}->test_progress( $test_id ); + $progress = $self->{_db}->test_progress( $test_id ); return; } ## end sub run diff --git a/lib/Zonemaster/Backend/Translator.pm b/lib/Zonemaster/Backend/Translator.pm index 32b974a71..7ef59783a 100644 --- a/lib/Zonemaster/Backend/Translator.pm +++ b/lib/Zonemaster/Backend/Translator.pm @@ -16,21 +16,7 @@ require Zonemaster::Engine::Logger::Entry; extends 'Zonemaster::Engine::Translator'; sub translate_tag { - my ( $self, $hashref, $browser_lang ) = @_; - - # Workaround for broken Zonemaster::Engine::translate_tag in Zonemaster-Engine 3.1.2. - # Make locale really be set. Fix that makes translation work on FreeBSD 12.1. Solution copied from - # CLI.pm in the Zonemaster-CLI repository. - undef $ENV{LANGUAGE}; - $ENV{LC_ALL} = $self->locale; - if ( not defined setlocale( LC_MESSAGES, "" ) ) { - warn sprintf "Warning: setting locale category LC_MESSAGES to %s failed (is it installed on this system?).", - $ENV{LANGUAGE} || $ENV{LC_ALL} || $ENV{LC_MESSAGES}; - } - if ( not defined setlocale( LC_CTYPE, "" ) ) { - warn sprintf "Warning: setting locale category LC_CTYPE to %s failed (is it installed on this system?)." , - $ENV{LC_ALL} || $ENV{LC_CTYPE}; - } + my ( $self, $hashref ) = @_; my $entry = Zonemaster::Engine::Logger::Entry->new( %{ $hashref } ); my $octets = Zonemaster::Engine::Translator::translate_tag( $self, $entry ); diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 031160bf8..d5595b947 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -7,34 +7,93 @@ use warnings; use 5.14.2; use Exporter qw( import ); +use File::Spec::Functions qw( file_name_is_absolute ); use JSON::Validator::Joi; use Readonly; +use Locale::TextDomain qw[Zonemaster-Backend]; +use Zonemaster::Engine::Net::IP; our @EXPORT_OK = qw( - untaint_engine_type + untaint_abs_path untaint_bool + untaint_engine_type + untaint_ip_address + untaint_ipv4_address + untaint_ipv6_address + untaint_host + untaint_ldh_domain + untaint_locale_tag + untaint_mariadb_database + untaint_mariadb_user + untaint_non_negative_int + untaint_password + untaint_postgresql_ident + untaint_profile_name + untaint_strictly_positive_int + untaint_strictly_positive_millis ); our %EXPORT_TAGS = ( untaint => [ qw( - untaint_engine_type + untaint_abs_path untaint_bool + untaint_engine_type + untaint_ip_address + untaint_ipv4_address + untaint_ipv6_address + untaint_host + untaint_ldh_domain + untaint_locale_tag + untaint_mariadb_database + untaint_mariadb_user + untaint_non_negative_int + untaint_password + untaint_postgresql_ident + untaint_profile_name + untaint_strictly_positive_int + untaint_strictly_positive_millis ) ], ); +# Does not check value ranges within the groups Readonly my $IPV4_RE => qr/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/; -Readonly my $IPV6_RE => qr/^([0-9a-f]{1,4}:[0-9a-f:]{1,}(:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?)$|([0-9a-f]{1,4}::[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/i; - -Readonly my $API_KEY_RE => qr/^[a-z0-9-_]{1,512}$/i; -Readonly my $CLIENT_ID_RE => qr/^[a-z0-9-+~_.: ]{1,50}$/i; -Readonly my $CLIENT_VERSION_RE => qr/^[a-z0-9-+~_.: ]{1,50}$/i; -Readonly my $DIGEST_RE => qr/^[a-f0-9]{40}$|^[a-f0-9]{64}$/i; -Readonly my $ENGINE_TYPE_RE => qr/^(?:mysql|postgresql|sqlite)$/i; -Readonly my $IPADDR_RE => qr/^$|$IPV4_RE|$IPV6_RE/; -Readonly my $JSONRPC_METHOD_RE => qr/^[a-z0-9_-]*$/i; -Readonly my $LANGUAGE_RE => qr/^[a-z]{2}(_[A-Z]{2})?$/; + +# Does not check the length and number of the hex groups, nor the value ranges in the IPv4 groups +Readonly my $IPV6_RE => qr/^[0-9a-f:]*:[0-9a-f:]+(:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?$/i; + +Readonly my $API_KEY_RE => qr/^[a-z0-9-_]{1,512}$/i; +Readonly my $CLIENT_ID_RE => qr/^[a-z0-9-+~_.: ]{1,50}$/i; +Readonly my $CLIENT_VERSION_RE => qr/^[a-z0-9-+~_.: ]{1,50}$/i; +Readonly my $DIGEST_RE => qr/^[a-f0-9]{40}$|^[a-f0-9]{64}$|^[a-f0-9]{96}$/i; +Readonly my $ENGINE_TYPE_RE => qr/^(?:mysql|postgresql|sqlite)$/i; +Readonly my $IPADDR_RE => qr/^$|$IPV4_RE|$IPV6_RE/; +Readonly my $JSONRPC_METHOD_RE => qr/^[a-z0-9_-]*$/i; +Readonly my $LANGUAGE_RE => qr/^[a-z]{2}(_[A-Z]{2})?$/; +Readonly my $LDH_DOMAIN_RE1 => qr{^[a-z0-9-.]{1,253}[.]?$}i; +Readonly my $LDH_DOMAIN_RE2 => qr{^(?:[.]|[^.]{1,63}(?:[.][^.]{1,63})*[.]?)$}; +Readonly my $LOCALE_TAG_RE => qr/^[a-z]{2}_[A-Z]{2}$/; +Readonly my $MARIADB_DATABASE_LENGTH_RE => qr/^.{1,64}$/; + +# See: https://mariadb.com/kb/en/identifier-names/#unquoted +Readonly my $MARIADB_IDENT_RE => qr/^[0-9a-z\$_]+$/i; +Readonly my $MARIADB_USER_LENGTH_RE => qr/^.{1,80}$/u; + +# Up to 5 and 3 digits in the integer and fraction components respectively +Readonly my $MILLIS_RE => qr/^(?:0|[1-9][0-9]{0,4})(?:[.][0-9]{1,3})?$/; + +# Up to 5 digits +Readonly my $NON_NEGATIVE_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; + +# At least one non-zero digit +Readonly my $NON_ZERO_NUM_RE => qr/[1-9]/; + +# Printable ASCII but first character must not be space or '<' +Readonly my $PASSWORD_RE => qr/^(?:[\x21-\x3b\x3d-\x7e][\x20-\x7e]{0,99})?$/; + +# See: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS +Readonly my $POSTGRESQL_IDENT_RE => qr/^[a-z_][a-z0-9_\$]{0,62}$/i; Readonly my $PROFILE_NAME_RE => qr/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$/i; Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; @@ -71,24 +130,60 @@ sub client_version { return joi->string->regex( $CLIENT_VERSION_RE ); } sub domain_name { - return joi->string->regex( $RELAXED_DOMAIN_NAME_RE ); + return { + type => 'string', + pattern => $RELAXED_DOMAIN_NAME_RE, + 'x-error-message' => N__ 'The domain name contains a character or characters not supported' + }; } sub ds_info { - return joi->object->strict->props( - digest => joi->string->regex($DIGEST_RE)->required, - algorithm => joi->integer->min(0)->required, - digtype => joi->integer->min(0)->required, - keytag => joi->integer->min(0)->required, - ); + return { + type => 'object', + additionalProperties => 0, + required => [ 'digest', 'algorithm', 'digtype', 'keytag' ], + properties => { + digest => { + type => 'string', + pattern => $DIGEST_RE, + 'x-error-message' => N__ 'Invalid digest format' + }, + algorithm => { + type => 'number', + minimum => 0, + 'x-error-message' => N__ 'Algorithm must be a positive integer' + }, + digtype => { + type => 'number', + minimum => 0, + 'x-error-message' => N__ 'Digest type must be a positive integer' + }, + keytag => { + type => 'number', + minimum => 0, + 'x-error-message' => N__ 'Keytag must be a positive integer' + } + } + }; } sub ip_address { - return joi->string->regex( $IPADDR_RE ); + return { + type => 'string', + pattern => $IPADDR_RE, + 'x-error-message' => N__ 'Invalid IP address', + }; } sub nameserver { - return joi->object->strict->props( - ns => joi->string->required, - ip => ip_address() - ); + return { + type => 'object', + required => [ 'ns' ], + additionalProperties => 0, + properties => { + ns => { + type => 'string' + }, + ip => ip_address + } + }; } sub priority { return joi->integer; @@ -103,7 +198,11 @@ sub test_id { return joi->string->regex( $TEST_ID_RE ); } sub language_tag { - return joi->string->regex( $LANGUAGE_RE ); + return { + type => 'string', + pattern => $LANGUAGE_RE, + 'x-error-message' => N__ 'Invalid language tag format' + }; } sub username { return joi->string->regex( $USERNAME_RE ); @@ -131,6 +230,13 @@ This module contains a set of procedures for validating and untainting strings. These procedures all take a possibly tainted single string argument. If the string is accepted an untainted copy of the string is returned. +=cut + +sub untaint_abs_path { + my ( $value ) = @_; + return _untaint_pred( $value, \&file_name_is_absolute ); +} + =head2 untaint_engine_type Accepts the strings C<"MySQL">, C<"PostgreSQL"> and C<"SQLite">, @@ -143,6 +249,122 @@ sub untaint_engine_type { return _untaint_pat( $value , $ENGINE_TYPE_RE ); } +=head2 untaint_ip_address + +Accepts an IPv4 or IPv6 address. + +=cut + +sub untaint_ip_address { + my ( $value ) = @_; + return untaint_ipv4_address( $value ) // untaint_ipv6_address( $value ); +} + +=head2 untaint_ipv4_address + +Accepts an IPv4 address. + +=cut + +sub untaint_ipv4_address { + my ( $value ) = @_; + if ( $value =~ /($IPV4_RE)/ + && Zonemaster::Engine::Net::IP::ip_is_ipv4( $value ) ) + { + return $1; + } + return; +} + +=head2 untaint_ipv6_address + +Accepts an IPv6 address. + +=cut + +sub untaint_ipv6_address { + my ( $value ) = @_; + if ( $value =~ /($IPV6_RE)/ + && Zonemaster::Engine::Net::IP::ip_is_ipv6( $value ) ) + { + return $1; + } + return; +} + +=head2 untaint_host + +Accepts an LDH domain name or an IPv4 or IPv6 address. + +=cut + +sub untaint_host { + my ( $value ) = @_; + return untaint_ldh_domain( $value ) // untaint_ip_address( $value ); +} + +=head2 untaint_ldh_domain + +Accepts an LDH domain name. + +=cut + +sub untaint_ldh_domain { + my ( $value ) = @_; + return _untaint_pat( $value, $LDH_DOMAIN_RE1, $LDH_DOMAIN_RE2 ); +} + +=head2 untaint_locale_tag + +Accepts a locale tag. + +=cut + +sub untaint_locale_tag { + my ( $value ) = @_; + return _untaint_pat( $value, $LOCALE_TAG_RE ); +} + +sub untaint_mariadb_database { + my ( $value ) = @_; + return _untaint_pat( $value, $MARIADB_IDENT_RE, $MARIADB_DATABASE_LENGTH_RE ); +} + +sub untaint_mariadb_user { + my ( $value ) = @_; + return _untaint_pat( $value, $MARIADB_IDENT_RE, $MARIADB_USER_LENGTH_RE ); +} + +sub untaint_password { + my ( $value ) = @_; + return _untaint_pat( $value, $PASSWORD_RE ); +} + +sub untaint_strictly_positive_int { + my ( $value ) = @_; + return _untaint_pat( $value, $NON_NEGATIVE_INT_RE, $NON_ZERO_NUM_RE ); +} + +sub untaint_strictly_positive_millis { + my ( $value ) = @_; + return _untaint_pat( $value, $MILLIS_RE, $NON_ZERO_NUM_RE ); +} + +sub untaint_postgresql_ident { + my ( $value ) = @_; + return _untaint_pat( $value, $POSTGRESQL_IDENT_RE ); +} + +sub untaint_non_negative_int { + my ( $value ) = @_; + return _untaint_pat( $value, $NON_NEGATIVE_INT_RE ); +} + +sub untaint_profile_name { + my ( $value ) = @_; + return _untaint_pat( $value, $PROFILE_NAME_RE ); +} + sub untaint_bool { my ( $value ) = @_; @@ -153,13 +375,30 @@ sub untaint_bool { } sub _untaint_pat { - my ( $value, $pattern ) = @_; + my ( $value, @patterns ) = @_; - if ( $value =~ /($pattern)/ ) { - return $1; + for my $pattern ( @patterns ) { + if ( $value !~ /($pattern)/ ) { + return; + } } - return; + $value =~ qr/(.*)/; + return $1; +} + +sub _untaint_pred { + my ( $value, $predicate ) = @_; + + if ( $predicate->( $value ) ) { + $value =~ qr/(.*)/; + return $1; + } + else { + return; + } } + + 1; diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl deleted file mode 100644 index 63a230131..000000000 --- a/script/create_db_mysql.pl +++ /dev/null @@ -1,105 +0,0 @@ -use strict; -use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); - -use Zonemaster::Backend::Config; -use Zonemaster::Backend::DB::MySQL; - -my $config = Zonemaster::Backend::Config->load_config(); -if ( $config->DB_engine ne 'MySQL' ) { - die "The configuration file does not contain the MySQL backend"; -} -my $dbh = Zonemaster::Backend::DB::MySQL->new( { config => $config } )->dbh; - -sub create_db { - - #################################################################### - # TEST RESULTS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS test_specs CASCADE' ); - - $dbh->do( 'DROP TABLE IF EXISTS test_results CASCADE' ); - - $dbh->do( - 'CREATE TABLE test_results ( - id integer AUTO_INCREMENT PRIMARY KEY, - hash_id VARCHAR(16) DEFAULT NULL, - domain varchar(255) NOT NULL, - batch_id integer NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - test_start_time TIMESTAMP NULL DEFAULT NULL, - test_end_time TIMESTAMP NULL DEFAULT NULL, - priority integer DEFAULT 10, - queue integer DEFAULT 0, - progress integer DEFAULT 0, - params_deterministic_hash character varying(32), - params blob NOT NULL, - results blob DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, - nb_retries integer NOT NULL DEFAULT 0 - ) ENGINE=InnoDB - ' - ); - - $dbh->do( - 'CREATE TRIGGER before_insert_test_results - BEFORE INSERT ON test_results - FOR EACH ROW - BEGIN - IF new.hash_id IS NULL OR new.hash_id=\'\' - THEN - SET new.hash_id = SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16); - END IF; - END; - ' - ); - - $dbh->do( - 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' - ); - - $dbh->do( - 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' - ); - - $dbh->do( - 'CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)' - ); - - $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); - - #################################################################### - # BATCH JOBS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS batch_jobs CASCADE' ); - - $dbh->do( - 'CREATE TABLE batch_jobs ( - id integer AUTO_INCREMENT PRIMARY KEY, - username character varying(50) NOT NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL - ) ENGINE=InnoDB; - ' - ); - - #################################################################### - # USERS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS users CASCADE' ); - - $dbh->do( - 'CREATE TABLE users ( - id integer AUTO_INCREMENT primary key, - username varchar(128), - api_key varchar(512), - user_info blob DEFAULT NULL - ) ENGINE=InnoDB; - ' - ); -} - -create_db(); diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl deleted file mode 100644 index 1d943eeae..000000000 --- a/script/create_db_postgresql_9.3.pl +++ /dev/null @@ -1,132 +0,0 @@ -use strict; -use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); - -use Zonemaster::Backend::Config; -use Zonemaster::Backend::DB::PostgreSQL; - -my $config = Zonemaster::Backend::Config->load_config(); -if ( $config->DB_engine ne 'PostgreSQL' ) { - die "The configuration file does not contain the MySQL backend"; -} -my $dbh = Zonemaster::Backend::DB::PostgreSQL->new( { config => $config } )->dbh; -my $db_user = $config->POSTGRESQL_user; - -sub create_db { - - #################################################################### - # TEST RESULTS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS test_specs CASCADE' ); - $dbh->do( 'DROP SEQUENCE IF EXISTS test_specs_id_seq' ); - - $dbh->do( 'DROP TABLE IF EXISTS test_results CASCADE' ); - $dbh->do( 'DROP SEQUENCE IF EXISTS test_results_id_seq' ); - - $dbh->do( - 'CREATE SEQUENCE test_results_id_seq - INCREMENT BY 1 - NO MAXVALUE - NO MINVALUE - CACHE 1 - ' - ); - - $dbh->do( "ALTER TABLE public.test_results_id_seq OWNER TO $db_user" ); - - $dbh->do( - 'CREATE TABLE test_results ( - id integer DEFAULT nextval(\'test_results_id_seq\'::regclass) primary key, - hash_id VARCHAR(16) DEFAULT substring(md5(random()::text || clock_timestamp()::text) from 1 for 16) NOT NULL, - batch_id integer DEFAULT NULL, - creation_time timestamp without time zone DEFAULT NOW() NOT NULL, - test_start_time timestamp without time zone DEFAULT NULL, - test_end_time timestamp without time zone DEFAULT NULL, - priority integer DEFAULT 10, - queue integer DEFAULT 0, - progress integer DEFAULT 0, - params_deterministic_hash character varying(32), - params json NOT NULL, - results json DEFAULT NULL, - nb_retries integer NOT NULL DEFAULT 0 - ) - ' - ); - - $dbh->do( - 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' - ); - - $dbh->do( - 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' - ); - - $dbh->do( - 'CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)' - ); - - $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated'))" ); - - - $dbh->do( "ALTER TABLE test_results OWNER TO $db_user" ); - - #################################################################### - # BATCH JOBS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS batch_jobs CASCADE' ); - $dbh->do( 'DROP SEQUENCE IF EXISTS batch_jobs_id_seq' ); - - $dbh->do( - 'CREATE SEQUENCE batch_jobs_id_seq - INCREMENT BY 1 - NO MAXVALUE - NO MINVALUE - CACHE 1 - ' - ); - - $dbh->do( "ALTER TABLE public.batch_jobs_id_seq OWNER TO $db_user" ); - - $dbh->do( - 'CREATE TABLE batch_jobs ( - id integer DEFAULT nextval(\'batch_jobs_id_seq\'::regclass) primary key, - username character varying(50) NOT NULL, - creation_time timestamp without time zone DEFAULT NOW() NOT NULL - ) - ' - ); - $dbh->do( "ALTER TABLE batch_jobs OWNER TO $db_user" ); - - #################################################################### - # USERS - #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS users CASCADE' ); - $dbh->do( 'DROP SEQUENCE IF EXISTS users_id_seq' ); - - $dbh->do( - 'CREATE SEQUENCE users_id_seq - INCREMENT BY 1 - NO MAXVALUE - NO MINVALUE - CACHE 1 - ' - ); - - $dbh->do( "ALTER TABLE public.users_id_seq OWNER TO $db_user" ); - - $dbh->do( - 'CREATE TABLE users ( - id integer DEFAULT nextval(\'users_id_seq\'::regclass) primary key, - user_info json DEFAULT NULL - ) - ' - ); - $dbh->do( "ALTER TABLE users OWNER TO $db_user" ); - -} - -create_db(); diff --git a/script/zmb b/script/zmb index 72d8e0dd3..19613feb7 100755 --- a/script/zmb +++ b/script/zmb @@ -147,6 +147,7 @@ sub cmd_get_language_tags { --ipv4 true|false|null --ipv6 true|false|null --nameserver DOMAIN_NAME:IP_ADDRESS + --nameserver DOMAIN_NAME # Trailing colon is optional when not specifing IP_ADDRESS --ds-info DS_INFO --client-id CLIENT_ID --client-version CLIENT_VERSION @@ -154,9 +155,9 @@ sub cmd_get_language_tags { DS_INFO is a comma separated list of key-value pairs. The expected pairs are: - keytag=UNSIGNED_INTEGER - algorithm=UNSIGNED_INTEGER - digtype=UNSIGNED_INTEGER + keytag=NON_NEGATIVE_INTEGER + algorithm=NON_NEGATIVE_INTEGER + digtype=NON_NEGATIVE_INTEGER digest=HEX_STRING =cut @@ -214,6 +215,7 @@ sub cmd_start_domain_test { my @nameserver_objects; for my $domain_ip_pair ( @opt_nameserver ) { my ( $domain, $ip ) = split /:/, $domain_ip_pair, 2; + $ip //= ""; push @nameserver_objects, { ns => $domain, @@ -243,12 +245,14 @@ sub cmd_start_domain_test { } + + =head2 test_progress zmb [GLOBAL OPTIONS] test_progress [OPTIONS] Options: - --testid TEST_ID + --test-id TEST_ID =cut @@ -256,15 +260,45 @@ sub cmd_test_progress { my @opts = @_; my $opt_lang; - my $opt_testid; - GetOptionsFromArray( \@opts, 'testid|t=s' => \$opt_testid, ) - or pod2usage( 2 ); + my $opt_test_id; + GetOptionsFromArray( + \@opts, + 'test-id|t=s' => \$opt_test_id, + ) or pod2usage( 2 ); return to_jsonrpc( id => 1, method => 'test_progress', params => { - test_id => $opt_testid, + test_id => $opt_test_id, + }, + ); +} + + +=head2 get_test_params + + zmb [GLOBAL OPTIONS] get_test_params [OPTIONS] + + Options: + --test-id TEST_ID + +=cut + +sub cmd_get_test_params { + my @opts = @_; + + my $opt_test_id; + GetOptionsFromArray( # + \@opts, + 'test-id|t=s' => \$opt_test_id, + ) or pod2usage( 2 ); + + return to_jsonrpc( + id => 1, + method => 'get_test_params', + params => { + test_id => $opt_test_id, }, ); } @@ -275,7 +309,7 @@ sub cmd_test_progress { zmb [GLOBAL OPTIONS] get_test_results [OPTIONS] Options: - --testid TEST_ID + --test-id TEST_ID --lang LANGUAGE =cut @@ -284,18 +318,18 @@ sub cmd_get_test_results { my @opts = @_; my $opt_lang; - my $opt_testid; + my $opt_test_id; GetOptionsFromArray( \@opts, - 'testid|t=s' => \$opt_testid, - 'lang|l=s' => \$opt_lang, + 'test-id|t=s' => \$opt_test_id, + 'lang|l=s' => \$opt_lang, ) or pod2usage( 2 ); return to_jsonrpc( id => 1, method => 'get_test_results', params => { - id => $opt_testid, + id => $opt_test_id, language => $opt_lang, }, ); @@ -308,7 +342,7 @@ sub cmd_get_test_results { Options: --domain DOMAIN_NAME - --nameserver true|false|null + --filter all|delegated|undelegated --offset COUNT --limit COUNT @@ -316,7 +350,7 @@ sub cmd_get_test_results { sub cmd_get_test_history { my @opts = @_; - my $opt_nameserver; + my $opt_filter; my $opt_domain; my $opt_offset; my $opt_limit; @@ -324,7 +358,7 @@ sub cmd_get_test_history { GetOptionsFromArray( \@opts, 'domain|d=s' => \$opt_domain, - 'nameserver|n=s' => \$opt_nameserver, + 'filter|n=s' => \$opt_filter, 'offset|o=i' => \$opt_offset, 'limit|l=i' => \$opt_limit, ) or pod2usage( 2 ); @@ -335,8 +369,11 @@ sub cmd_get_test_history { }, ); - if ( $opt_nameserver ) { - $params{frontend_params}{nameservers} = json_tern( $opt_nameserver ); + if ( $opt_filter ) { + unless ( $opt_filter =~ /^(?:all|delegated|undelegated)$/ ) { + die 'Illegal filter value. Expects "all", "delegated" or "undelegated" '; + } + $params{filter} = $opt_filter; } if ( defined $opt_offset ) { @@ -387,6 +424,155 @@ sub cmd_add_api_user { } +=head2 add_batch_job + + zmb [GLOBAL OPTIONS] add_batch_job [OPTIONS] + + Options: + --username USERNAME + --api-key API_KEY + --domain DOMAIN_NAME + --ipv4 true|false|null + --ipv6 true|false|null + --nameserver DOMAIN_NAME:IP_ADDRESS + --nameserver DOMAIN_NAME # Trailing colon is optional when not specifing IP_ADDRESS + --ds-info DS_INFO + --client-id CLIENT_ID + --client-version CLIENT_VERSION + --profile PROFILE_NAME + + "--domain" is repeated for each domain to be tested. + "--nameserver" can be repeated for each name server. + "--ds-info" can be repeated for each DS record. + + DS_INFO is a comma separated list of key-value pairs. The expected pairs are: + + keytag=NON_NEGATIVE_INTEGER + algorithm=NON_NEGATIVE_INTEGER + digtype=NON_NEGATIVE_INTEGER + digest=HEX_STRING + +=cut + +sub cmd_add_batch_job { + my @opts = @_; + + my $opt_username; + my $opt_api_key; + my @opt_nameserver; + my @opt_domains; + my $opt_client_id; + my $opt_client_version; + my @opt_ds_info; + my $opt_ipv4; + my $opt_ipv6; + my $opt_profile; + GetOptionsFromArray( + \@opts, + 'username|u=s' => \$opt_username, + 'api-key|a=s' => \$opt_api_key, + 'domain|d=s' => \@opt_domains, + 'nameserver|n=s' => \@opt_nameserver, + 'client-id=s' => \$opt_client_id, + 'client-version=s' => \$opt_client_version, + 'ds-info=s' => \@opt_ds_info, + 'ipv4=s' => \$opt_ipv4, + 'ipv6=s' => \$opt_ipv6, + 'profile=s' => \$opt_profile, + ) or pod2usage( 2 ); + + + my %params = ( domains => \@opt_domains ); + + $params{username} = $opt_username; + $params{api_key} = $opt_api_key; + + if ( $opt_client_id ) { + $params{test_params}{client_id} = $opt_client_id; + } + + if ( $opt_client_version ) { + $params{test_params}{client_version} = $opt_client_version; + } + + if ( @opt_ds_info ) { + my @info_objects; + for my $property_value_pairs ( @opt_ds_info ) { + my %info_object; + for my $pair ( split /,/, $property_value_pairs ) { + my ( $property, $value ) = split /=/, $pair; + if ( $property =~ /^(?:keytag|algorithm|digtype)$/ ) { + $value = 0 + $value; + } + $info_object{$property} = $value; + } + push @info_objects, \%info_object; + } + $params{test_params}{ds_info} = \@info_objects; + } + + if ( @opt_nameserver ) { + my @nameserver_objects; + for my $domain_ip_pair ( @opt_nameserver ) { + my ( $domain, $ip ) = split /:/, $domain_ip_pair, 2; + $ip //= ""; + push @nameserver_objects, + { + ns => $domain, + ip => $ip, + }; + } + $params{test_params}{nameservers} = \@nameserver_objects; + } + + if ( $opt_ipv4 ) { + $params{test_params}{ipv4} = json_tern( $opt_ipv4 ); + } + + if ( $opt_ipv6 ) { + $params{test_params}{ipv6} = json_tern( $opt_ipv6 ); + } + + if ( $opt_profile ) { + $params{test_params}{profile} = $opt_profile; + } + + return to_jsonrpc( + id => 1, + method => 'add_batch_job', + params => \%params, + ); +} + + +=head2 get_batch_job_result + + zmb [GLOBAL OPTIONS] get_batch_job_result [OPTIONS] + + Options: + --batch-id BATCH-ID + +=cut + +sub cmd_get_batch_job_result { + my @opts = @_; + + my $opt_batch_id; + GetOptionsFromArray( + \@opts, + 'batch-id|i=s' => \$opt_batch_id, + ) or pod2usage( 2 ); + + return to_jsonrpc( + id => 1, + method => 'get_batch_job_result', + params => { + batch_id => $opt_batch_id, + }, + ); +} + + sub show_commands { my %specials = ( man => 'Show the full manual page.', @@ -429,7 +615,7 @@ sub json_tern { return undef; } else { - die "unknown ternary value"; + die 'Illegal value. Expects "true", "false" or "null" '; } } diff --git a/script/zmtest b/script/zmtest index a36aed15d..6d824af59 100755 --- a/script/zmtest +++ b/script/zmtest @@ -72,7 +72,7 @@ fi # Wait for test to finish while true do - output="$(zmb "${server_url}" test_progress --testid "${testid}")" || exit $? + output="$(zmb "${server_url}" test_progress --test-id "${testid}")" || exit $? progress="$(printf "%s" "${output}" | "${JQ}" -r .result)" || exit $? printf "\r${progress}%% done" >&2 if [ "${progress}" -eq 100 ] ; then @@ -83,4 +83,4 @@ do done # Get test results -zmb "${server_url}" get_test_results --testid "${testid}" --lang "${lang}" +zmb "${server_url}" get_test_results --test-id "${testid}" --lang "${lang}" diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index f2bc43203..daae45a32 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -19,10 +19,14 @@ use Plack::Response; use Router::Simple::Declare; use Try::Tiny; -BEGIN { $ENV{PERL_JSON_BACKEND} = 'JSON::PP' }; +BEGIN { + $ENV{PERL_JSON_BACKEND} = 'JSON::PP'; + undef $ENV{LANGUAGE}; +}; use Zonemaster::Backend::RPCAPI; use Zonemaster::Backend::Config; +use Zonemaster::Backend::Metrics; local $| = 1; @@ -45,17 +49,24 @@ Log::Any::Adapter->set( 'Screen', min_level => get_loglevel(), stderr => 1, + newline => 1, callbacks => sub { my %args = @_; - $args{message} = sprintf "%s [%d] %s - %s\n", strftime( "%FT%TZ", gmtime ), $PID, uc $args{level}, $args{message}; + $args{message} = sprintf "%s [%d] %s - %s", strftime( "%FT%TZ", gmtime ), $PID, uc $args{level}, $args{message}; }, ], ] ), ); +$SIG{__WARN__} = sub { + $log->warning(map s/^\s+|\s+$//gr, map s/\n/ /gr, @_); +}; + my $config = Zonemaster::Backend::Config->load_config(); +Zonemaster::Backend::Metrics->setup($config->METRICS_statsd_host, $config->METRICS_statsd_port); + builder { enable sub { my $app = shift; @@ -161,14 +172,17 @@ my $rpcapi_app = sub { }; if ($json_error eq '') { - my $errors = Zonemaster::Backend::RPCAPI->jsonrpc_validate($content); + my $errors = $handler->jsonrpc_validate($content); if ($errors ne '') { $res = Plack::Response->new(200); $res->content_type('application/json'); $res->body( encode_json($errors) ); $res->finalize; } else { - $dispatch->handle_psgi($env, $env->{REMOTE_ADDR}); + $res = $dispatch->handle_psgi($env, $env->{REMOTE_ADDR}); + my $status = Zonemaster::Backend::Metrics->code_to_status(decode_json(@{@$res[2]}[0])->{error}->{code}); + Zonemaster::Backend::Metrics::increment("zonemaster.rpcapi.requests.$content->{method}.$status"); + $res; } } else { $res = Plack::Response->new(200); diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index f3fa56d73..859b24721 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -5,6 +5,7 @@ use warnings; use Zonemaster::Backend::TestAgent; use Zonemaster::Backend::Config; +use Zonemaster::Backend::Metrics; use Parallel::ForkManager; use Daemon::Control; @@ -16,7 +17,7 @@ use English; use Pod::Usage; use Getopt::Long; use POSIX; -use Time::HiRes qw[time sleep]; +use Time::HiRes qw[time sleep gettimeofday tv_interval]; use sigtrap qw(die normal-signals); ### @@ -78,9 +79,10 @@ sub log_dispatcher_dup_stdout { 'Handle', handle => $handle, min_level => $min_level, + newline => 1, callbacks => sub { my %args = @_; - $args{message} = sprintf "%s: %s\n", uc $args{level}, $args{message}; + $args{message} = sprintf "%s [%d] %s - %s", strftime( "%FT%TZ", gmtime ), $PID, uc $args{level}, $args{message}; }, ], ] @@ -99,15 +101,20 @@ sub log_dispatcher_file { filename => $log_file, mode => '>>', min_level => $min_level, + newline => 1, callbacks => sub { my %args = @_; - $args{message} = sprintf "%s [%d] %s - %s\n", strftime( "%FT%TZ", gmtime ), $PID, uc $args{level}, $args{message}; + $args{message} = sprintf "%s [%d] %s - %s", strftime( "%FT%TZ", gmtime ), $PID, uc $args{level}, $args{message}; }, ], ] ); } +$SIG{__WARN__} = sub { + $log->warning(map s/^\s+|\s+$//gr, map s/\n/ /gr, @_); +}; + ### ### Actual functionality ### @@ -126,26 +133,48 @@ sub main { local $SIG{TERM} = $catch_sigterm; my $agent = Zonemaster::Backend::TestAgent->new( { config => $self->config } ); + # Disconnect from database in parent to avoid sharing the connection between children + $agent->{_db}->dbh->disconnect; while ( !$caught_sigterm ) { + my $cleanup_timer = [ gettimeofday ]; + $self->pm->reap_finished_children(); # Reaps terminated child processes $self->pm->on_wait(); # Sends SIGKILL to overdue child processes - my $id = $self->db->get_test_request(); - $self->db->process_unfinished_tests(); + Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.maximum_processes", $self->pm->max_procs); + Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.running_processes", scalar($self->pm->running_procs)); + + Zonemaster::Backend::Metrics::timing("zonemaster.testagent.cleanup_duration_seconds", tv_interval($cleanup_timer) * 1000); + + my $fetch_test_timer = [ gettimeofday ]; + + my $id = $self->db->get_test_request( $self->config->ZONEMASTER_lock_on_queue ); + $self->db->process_unfinished_tests( + $self->config->ZONEMASTER_lock_on_queue, + $self->config->ZONEMASTER_max_zonemaster_execution_time, + ); + + Zonemaster::Backend::Metrics::timing("zonemaster.testagent.fetchtests_duration_seconds", tv_interval($fetch_test_timer) * 1000); if ( $id ) { $log->info( "Test found: $id" ); if ( $self->pm->start( $id ) == 0 ) { # Forks off child process $log->info( "Test starting: $id" ); + Zonemaster::Backend::Metrics::increment("zonemaster.testagent.tests_started"); + my $start_time = [ gettimeofday ]; eval { $agent->run( $id ) }; if ( $@ ) { chomp $@; + Zonemaster::Backend::Metrics::increment("zonemaster.testagent.tests_died"); $log->error( "Test died: $id: $@" ); + $self->db->process_dead_test( $id ) } else { + Zonemaster::Backend::Metrics::increment("zonemaster.testagent.tests_completed"); $log->info( "Test completed: $id" ); } + Zonemaster::Backend::Metrics::timing("zonemaster.testagent.tests_duration_seconds", tv_interval($start_time) * 1000); $agent->reset(); $self->pm->finish; # Terminates child process } @@ -163,24 +192,26 @@ sub main { } +# Initialize logging +my $dispatcher; +if ( $logfile eq '-' ) { + $dispatcher = log_dispatcher_dup_stdout( $loglevel ); +} +else { + $dispatcher = log_dispatcher_file( $loglevel, $logfile ); + print STDERR "zonemaster-testagent logging to file $logfile\n"; +} +Log::Any::Adapter->set( 'Dispatch', dispatcher => $dispatcher ); + # Make sure the environment is alright before forking my $initial_config; eval { - # Initialize logging - my $dispatcher; - if ( $logfile eq '-' ) { - $dispatcher = log_dispatcher_dup_stdout( $loglevel ); - } - else { - $dispatcher = log_dispatcher_file( $loglevel, $logfile ); - print STDERR "zonemaster-testagent logging to file $logfile\n"; - } - Log::Any::Adapter->set( 'Dispatch', dispatcher => $dispatcher ); - # Make sure we can load the configuration file $log->debug("Starting pre-flight check"); $initial_config = Zonemaster::Backend::Config->load_config(); + Zonemaster::Backend::Metrics->setup($initial_config->METRICS_statsd_host, $initial_config->METRICS_statsd_port); + # Validate the Zonemaster-Engine profile Zonemaster::Backend::TestAgent->new( { config => $initial_config } ); @@ -189,6 +220,7 @@ eval { $log->debug("Completed pre-flight check"); }; if ( $@ ) { + $log->critical( "Aborting startup: $@" ); print STDERR "Aborting startup: $@"; exit 1; } diff --git a/share/GNUmakefile b/share/GNUmakefile new file mode 100644 index 000000000..dd50d13d3 --- /dev/null +++ b/share/GNUmakefile @@ -0,0 +1,39 @@ +.POSIX: +.SUFFIXES: .po .mo +.PHONY: all check-msg-args dist extract-pot tidy-po show-fuzzy update-po + +POFILES := $(shell find . -maxdepth 1 -type f -name '*.po' -exec basename {} \;) +MOFILES := $(POFILES:%.po=%.mo) +POTFILE = Zonemaster-Backend.pot +PMFILES := $(shell find ../lib -type f -name '*.pm' | sort) + +all: $(MOFILES) + @echo + @echo Remember to make sure all of the above names are in the + @echo MANIFEST file, or they will not be installed. + @echo + +# Tidy the formatting of all PO files +tidy-po: + @tmpdir="`mktemp -d tidy-po.XXXXXXXX`" ;\ + trap 'rm -rf "$$tmpdir"' EXIT ;\ + for f in $(POFILES) ; do msgcat $$f -o $$tmpdir/$$f && mv -f $$tmpdir/$$f $$f ; done + +update-po: extract-pot + @for f in $(POFILES) ; do msgmerge --update --backup=none --quiet --no-location $(MSGMERGE_OPTS) $$f $(POTFILE) ; done + +extract-pot: + @xgettext --output $(POTFILE) --sort-by-file --add-comments --language=Perl --from-code=UTF-8 -k__ -k\$$__ -k%__ -k__x -k__n:1,2 -k__nx:1,2 -k__xn:1,2 -kN__ -kN__n:1,2 -k__p:1c,2 -k__np:1c,2,3 -kN__p:1c,2 -kN__np:1c,2,3 $(PMFILES) + +$(POTFILE): extract-pot + +.po.mo: + @msgfmt -o $@ $< + @mkdir -p locale/`basename $@ .mo`/LC_MESSAGES + @ln -vf $@ locale/`basename $@ .mo`/LC_MESSAGES/Zonemaster-Backend.mo + +show-fuzzy: + @for f in $(POFILES) ; do msgattrib --only-fuzzy $$f ; done + +check-msg-args: + @for f in $(POFILES) ; do ../util/check-msg-args $$f ; done diff --git a/share/Makefile b/share/Makefile new file mode 100644 index 000000000..2bc84bd34 --- /dev/null +++ b/share/Makefile @@ -0,0 +1,14 @@ +# This is a wrapper for BSD Make (FreeBSD) to execute +# GNU Make (gmake) and the primary makefile GNUmakefile. + +GNUMAKE ?= gmake +FILES != ls * + +# File targets should be evaluated by gmake. +.PHONY: all $(FILES) + +all: + @${GNUMAKE} $@ + +.DEFAULT: + @${GNUMAKE} $@ diff --git a/share/backend_config.ini b/share/backend_config.ini index 0268f5cf2..b4205db0a 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -2,7 +2,7 @@ # https://github.com/zonemaster/zonemaster-backend/blob/master/docs/Configuration.md [DB] -engine = MySQL +engine = SQLite polling_interval = 0.5 [MYSQL] @@ -26,11 +26,6 @@ database_file = /var/lib/zonemaster/db.sqlite #number_of_processes_for_batch_testing = 20 #age_reuse_previous_test = 600 -# WARNING: The following option is experimental and all edge cases are not fully tested. -# Do not use it (keep the default value "0"), or use it with care. -# -#maximal_number_of_retries=3 - [RPCAPI] # Uncomment to enable API method "add_api_user" @@ -47,3 +42,8 @@ locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE [PRIVATE PROFILES] #example_profile_2=/example/directory/test2_profile.json + +[METRICS] +# Uncoment the following option to enable the metrics feature +#statsd_host = localhost +#statsd_port = 8125 diff --git a/share/cleanup-mysql.sql b/share/cleanup-mysql.sql index 634e8db96..adaa29e14 100644 --- a/share/cleanup-mysql.sql +++ b/share/cleanup-mysql.sql @@ -1,4 +1,3 @@ -- Remove Zonemaster data from database DROP DATABASE zonemaster; DROP USER 'zonemaster'@'localhost'; -DROP USER 'zonemaster'@'%'; diff --git a/share/create_db.pl b/share/create_db.pl new file mode 100755 index 000000000..f49ff3e9c --- /dev/null +++ b/share/create_db.pl @@ -0,0 +1,15 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB; + +my $config = Zonemaster::Backend::Config->load_config(); +my $db_engine = $config->DB_engine; + +my $db_class = Zonemaster::Backend::DB->get_db_class( $db_engine ); + +my $db = $db_class->from_config( $config ); +$db->create_db(); diff --git a/share/create_db_sqlite.pl b/share/create_db_sqlite.pl deleted file mode 100644 index b84cfd73c..000000000 --- a/share/create_db_sqlite.pl +++ /dev/null @@ -1,17 +0,0 @@ -use strict; -use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); - -use Zonemaster::Backend::Config; -use Zonemaster::Backend::DB::SQLite; - -my $config = Zonemaster::Backend::Config->load_config(); -if ( $config->DB_engine ne 'SQLite' ) { - die "The configuration file does not contain the SQLite backend"; -} -my $db = Zonemaster::Backend::DB::SQLite->new( { config => $config } ); -$db->create_db(); diff --git a/share/da.po b/share/da.po new file mode 100644 index 000000000..f3e8bdd59 --- /dev/null +++ b/share/da.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-14 11:25+0200\n" +"PO-Revision-Date: 2021-10-14 11:25+0200\n" +"Last-Translator: mats.dufberg@iis.se\n" +"Language-Team: Zonemaster project\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + diff --git a/share/es.po b/share/es.po new file mode 100644 index 000000000..99ada3c56 --- /dev/null +++ b/share/es.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-14 11:25+0200\n" +"PO-Revision-Date: 2021-10-14 11:25+0200\n" +"Last-Translator: mats.dufberg@iis.se\n" +"Language-Team: Zonemaster project\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + diff --git a/share/fi.po b/share/fi.po new file mode 100644 index 000000000..2e41f10af --- /dev/null +++ b/share/fi.po @@ -0,0 +1,78 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-29 17:02+0100\n" +"PO-Revision-Date: 2021-11-29 17:07+0100\n" +"Last-Translator: mats.dufberg@iis.se\n" +"Language-Team: Zonemaster project\n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.0\n" + +msgid "Domain name required" +msgstr "Vaaditaan verkkotunnus" + +msgid "" +"The domain name is not a valid IDNA string and cannot be converted to an A-" +"label" +msgstr "" +"Verkkotunnus ei ole kelvollinen IDNA-merkkijono, eikä sitä voi muuntaa A-" +"tunnisteeksi" + +msgid "The domain name contains non-ascii characters and IDNA is not installed" +msgstr "" +"Verkkotunnuksen nimi sisältää muita kuin ascii-merkkejä, mutta IDNA:ta ei " +"ole asennettu" + +msgid "The domain name character(s) are not supported" +msgstr "Verkkotunnuksen sisältämiä merkkejä ei tueta" + +msgid "The domain name or label is too long" +msgstr "Verkkotunnus tai sen tunnisteet ovat liian pitkiä" + +msgid "Unknown profile" +msgstr "Tuntematon profiili" + +msgid "Invalid IP address" +msgstr "Virheellinen IP-osoite" + +msgid "Unkown language string" +msgstr "Tuntematon kielitunniste" + +msgid "Language string not unique" +msgstr "Kielitunniste ei ole yksilöivä" + +msgid "Invalid method parameter(s)." +msgstr "Virheelliset asetukset" + +msgid "Missing property" +msgstr "Kenttä puuttuu" + +msgid "" +"Warning: Zonemaster::LDNS not compiled with libidn, cannot handle non-ASCII " +"names correctly." +msgstr "" +"Varoitus: Zonemaster :: LDNS ei ole käännetty libidnillä, eikä pysty " +"käsittelemään ei-ASCII-nimiä oikein." + +msgid "The domain name contains a character or characters not supported" +msgstr "Verkkotunnus sisältää merkin tai merkkejä joita ei tueta" + +msgid "Invalid digest format" +msgstr "Virheellinen tiivisteen muoto" + +msgid "Algorithm must be a positive integer" +msgstr "Algoritmin on oltava positiivinen kokonaisluku" + +msgid "Digest type must be a positive integer" +msgstr "Tiivistetyypin (Digest type) on oltava positiivinen kokonaisluku" + +msgid "Keytag must be a positive integer" +msgstr "Tunnisteen (Keytag) on oltava positiivinen kokonaisluku" + +msgid "Invalid language tag format" +msgstr "Virheellinen kielitunnisteen muoto" + diff --git a/share/fr.po b/share/fr.po new file mode 100644 index 000000000..cea0e4f8f --- /dev/null +++ b/share/fr.po @@ -0,0 +1,77 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-14 11:25+0200\n" +"PO-Revision-Date: 2021-10-14 11:25+0200\n" +"Last-Translator: \n" +"Language-Team: Zonemaster project\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Domain name required" +msgstr "Nom de domaine requis" + +msgid "" +"The domain name is not a valid IDNA string and cannot be converted to an A-" +"label" +msgstr "" +"Le nom de domaine n'est pas une chaine IDNA valide et ne peut pas être " +"convertie en A-label" + +msgid "The domain name contains non-ascii characters and IDNA is not installed" +msgstr "" +"Le nom de domaine contient des caractères non ascii et IDNA n'est pas " +"installé" + +msgid "The domain name character(s) are not supported" +msgstr "Les caractères du nom de domaine ne sont pas supportés" + +msgid "The domain name or label is too long" +msgstr "Le nom de domaine ou les labels sont trop longs" + +msgid "Unknown profile" +msgstr "Profil inconnu" + +msgid "Invalid IP address" +msgstr "Adresse IP invalide" + +msgid "Unkown language string" +msgstr "Étiquette d'indentification de langue inconnue" + +msgid "Language string not unique" +msgstr "Étiquette d'indentification de langue non unique" + +msgid "Missing 'params' object" +msgstr "Objet 'params' manquant" + +msgid "Invalid method parameter(s)." +msgstr "Paramètres incorrects" + +msgid "Missing property" +msgstr "Champ manquant" + +msgid "" +"Warning: Zonemaster::LDNS not compiled with libidn, cannot handle non-ASCII " +"names correctly." +msgstr "" + +msgid "The domain name contains a character or characters not supported" +msgstr "Les caractères du nom de domaine ne sont pas supportés" + +msgid "Invalid digest format" +msgstr "Format de l'empreinte invalide" + +msgid "Algorithm must be a positive integer" +msgstr "L'algorithme doit être un entier positif" + +msgid "Digest type must be a positive integer" +msgstr "Le type d'empreinte doit être un entier positif" + +msgid "Keytag must be a positive integer" +msgstr "L'indentifiant doit être un entier positif" + +msgid "Invalid language tag format" +msgstr "Format de l'étiquette d'indentification de langue incorrect" diff --git a/share/initial-mysql.sql b/share/initial-mysql.sql deleted file mode 100644 index 3776f34db..000000000 --- a/share/initial-mysql.sql +++ /dev/null @@ -1,57 +0,0 @@ --- Initial setup for MySQL database -CREATE DATABASE zonemaster; -CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; -CREATE USER 'zonemaster'@'%' IDENTIFIED BY 'zonemaster'; - -USE zonemaster; -CREATE TABLE test_results ( - id integer AUTO_INCREMENT PRIMARY KEY, - hash_id VARCHAR(16) DEFAULT NULL, - domain varchar(255) NOT NULL, - batch_id integer NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - test_start_time TIMESTAMP NULL, - test_end_time TIMESTAMP NULL, - priority integer DEFAULT 10, - queue integer DEFAULT 0, - progress integer DEFAULT 0, - params_deterministic_hash character varying(32), - params blob NOT NULL, - results mediumblob DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, - nb_retries integer NOT NULL DEFAULT 0 -) Engine=InnoDB; - -CREATE INDEX test_results__hash_id ON test_results (hash_id); -CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash); -CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress); -CREATE INDEX test_results__progress ON test_results (progress); -CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated); - -DELIMITER // -CREATE TRIGGER before_insert_test_results - BEFORE INSERT ON test_results - FOR EACH ROW - BEGIN - IF new.hash_id IS NULL OR new.hash_id='' - THEN - SET new.hash_id = SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16); - END IF; - END// -DELIMITER // - -CREATE TABLE batch_jobs ( - id integer AUTO_INCREMENT PRIMARY KEY, - username character varying(50) NOT NULL, - creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL -) Engine=InnoDB; -CREATE TABLE users ( - id integer AUTO_INCREMENT primary key, - username varchar(128), - api_key varchar(512), - user_info blob DEFAULT NULL -) Engine=InnoDB; -GRANT ALL ON zonemaster.test_results TO 'zonemaster'; -GRANT LOCK TABLES ON zonemaster.* TO 'zonemaster'; -GRANT SELECT,UPDATE,INSERT ON zonemaster.batch_jobs TO 'zonemaster'; -GRANT SELECT,UPDATE,INSERT ON zonemaster.users TO 'zonemaster'; diff --git a/share/initial-postgres.sql b/share/initial-postgres.sql deleted file mode 100644 index 4852f0666..000000000 --- a/share/initial-postgres.sql +++ /dev/null @@ -1,43 +0,0 @@ --- Initial setup of PostgreSQL database -CREATE USER zonemaster WITH PASSWORD 'zonemaster'; -CREATE DATABASE zonemaster WITH ENCODING 'UTF8'; - -\c zonemaster - -CREATE TABLE test_results ( - id serial primary key, - hash_id VARCHAR(16) DEFAULT substring(md5(random()::text || clock_timestamp()::text) from 1 for 16) NOT NULL, - batch_id integer, - creation_time timestamp without time zone DEFAULT NOW() NOT NULL, - test_start_time timestamp without time zone, - test_end_time timestamp without time zone, - priority integer DEFAULT 10, - queue integer DEFAULT 0, - progress integer DEFAULT 0, - params_deterministic_hash varchar(32), - params json NOT NULL, - results json, - nb_retries integer NOT NULL DEFAULT 0 -); - -CREATE INDEX test_results__hash_id ON test_results (hash_id); -CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash); -CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress); -CREATE INDEX test_results__progress ON test_results (progress); -CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated')); - -CREATE TABLE batch_jobs ( - id serial PRIMARY KEY, - username varchar(50) NOT NULL, - creation_time timestamp without time zone NOT NULL DEFAULT NOW() -); - -CREATE TABLE users ( - id serial PRIMARY KEY, - user_info JSON -); - -ALTER TABLE test_results OWNER TO zonemaster; -ALTER TABLE batch_jobs OWNER TO zonemaster; -ALTER TABLE users OWNER TO zonemaster; -GRANT USAGE ON test_results_id_seq, batch_jobs_id_seq, users_id_seq TO zonemaster; diff --git a/share/nb.po b/share/nb.po new file mode 100644 index 000000000..c062d85df --- /dev/null +++ b/share/nb.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-14 11:25+0200\n" +"PO-Revision-Date: 2021-10-14 11:25+0200\n" +"Last-Translator: mats.dufberg@iis.se\n" +"Language-Team: Zonemaster project\n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + diff --git a/share/patch/README.txt b/share/patch/README.txt new file mode 100644 index 000000000..237571982 --- /dev/null +++ b/share/patch/README.txt @@ -0,0 +1,2 @@ +Find instructions on patching (upgrading) the Zonemaster database +on https://github.com/zonemaster/zonemaster-backend/blob/master/docs/Upgrade.md diff --git a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl similarity index 64% rename from share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl rename to share/patch/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl index 020d1cb71..1316d2a71 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/patch/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); @@ -13,7 +10,7 @@ if ( $config->DB_engine ne 'MySQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::MySQL->new( { config => $config } )->dbh; +my $dbh = Zonemaster::Backend::DB::MySQL->from_config( $config )->dbh; sub patch_db { @@ -25,18 +22,18 @@ sub patch_db { $dbh->do( 'UPDATE test_results SET hash_id = (SELECT SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16))' ); $dbh->do( 'ALTER TABLE test_results MODIFY hash_id VARCHAR(16) DEFAULT NULL NOT NULL' ); - + $dbh->do( - 'CREATE TRIGGER before_insert_test_results - BEFORE INSERT ON test_results - FOR EACH ROW - BEGIN - IF new.hash_id IS NULL OR new.hash_id=\'\' - THEN - SET new.hash_id = SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16); - END IF; - END; - ' + 'CREATE TRIGGER before_insert_test_results + BEFORE INSERT ON test_results + FOR EACH ROW + BEGIN + IF new.hash_id IS NULL OR new.hash_id=\'\' + THEN + SET new.hash_id = SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16); + END IF; + END; + ' ); } diff --git a/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl similarity index 83% rename from share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl rename to share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl index 6ecf99359..975b1afe5 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); @@ -13,7 +10,7 @@ if ( $config->DB_engine ne 'MySQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::MySQL->new( { config => $config } )->dbh; +my $dbh = Zonemaster::Backend::DB::MySQL->from_config( $config )->dbh; sub patch_db { #################################################################### diff --git a/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl similarity index 84% rename from share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl rename to share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl index 2082f6e31..aead54574 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl +++ b/share/patch/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); @@ -13,7 +10,7 @@ if ( $config->DB_engine ne 'MySQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::MySQL->new( { config => $config } )->dbh; +my $dbh = Zonemaster::Backend::DB::MySQL->from_config( $config )->dbh; sub patch_db { ############################################################################ diff --git a/share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl new file mode 100644 index 000000000..9ca8fb648 --- /dev/null +++ b/share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl @@ -0,0 +1,76 @@ +use strict; +use warnings; +use JSON::PP; + +use DBI qw(:utils); + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB::MySQL; + +my $config = Zonemaster::Backend::Config->load_config(); +if ( $config->DB_engine ne 'MySQL' ) { + die "The configuration file does not contain the MySQL backend"; +} +my $db = Zonemaster::Backend::DB::MySQL->from_config( $config ); +my $dbh = $db->dbh; + + +sub patch_db { + # Remove the trigger + $dbh->do( 'DROP TRIGGER IF EXISTS before_insert_test_results' ); + + # Set the "hash_id" field to NOT NULL + eval { + $dbh->do( 'ALTER TABLE test_results MODIFY COLUMN hash_id VARCHAR(16) NOT NULL' ); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + # Rename column "params_deterministic_hash" into "fingerprint" + # Since MariaDB 10.5.2 (2020-03-26) + # ALTER TABLE t1 RENAME COLUMN old_col TO new_col; + # Before that we need to use CHANGE COLUMN + eval { + $dbh->do('ALTER TABLE test_results CHANGE COLUMN params_deterministic_hash fingerprint CHARACTER VARYING(32)'); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + # Update index + eval { + # retrieve all indexes by key name + my $indexes = $dbh->selectall_hashref( 'SHOW INDEXES FROM test_results', 'Key_name' ); + if ( exists($indexes->{test_results__params_deterministic_hash}) ) { + $dbh->do( "DROP INDEX test_results__params_deterministic_hash ON test_results" ); + } + $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); + }; + print( "Error while updating the index: " . $@ ) if ($@); + + # Update the "undelegated" column + my $sth1 = $dbh->prepare('SELECT id, params from test_results', undef); + $sth1->execute; + while ( my $row = $sth1->fetchrow_hashref ) { + my $id = $row->{id}; + my $raw_params = decode_json($row->{params}); + my $ds_info_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{ds_info}}; + my $nameservers_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{nameservers}}; + my $undelegated = $ds_info_values > 0 || $nameservers_values > 0 || 0; + + $dbh->do('UPDATE test_results SET undelegated = ? where id = ?', undef, $undelegated, $id); + } + + + # remove the "user_info" column from the "users" table + # the IF EXISTS clause is available with MariaDB but not MySQL + eval { + $dbh->do( "ALTER TABLE users DROP COLUMN user_info" ); + }; + print( "Error while dropping the column: " . $@ ) if ($@); + + # remove the "nb_retries" column from the "test_results" table + eval { + $dbh->do( "ALTER TABLE test_results DROP COLUMN nb_retries" ); + }; + print( "Error while dropping the column: " . $@ ) if ($@); +} + +patch_db(); diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl similarity index 84% rename from share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl rename to share/patch/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl index 344287a69..093966a09 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/patch/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); @@ -13,7 +10,7 @@ if ( $config->DB_engine ne 'MySQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::MySQL->new( { config => $config } )->dbh; +my $dbh = Zonemaster::Backend::DB::MySQL->from_config( $config )->dbh; sub patch_db { diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl similarity index 82% rename from share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl rename to share/patch/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl index 98f70b108..206d29f97 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/patch/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); @@ -13,7 +10,7 @@ if ( $config->DB_engine ne 'PostgreSQL' ) { die "The configuration file does not contain the PostgreSQL backend"; } -my $dbh = Zonemaster::Backend::DB::PostgreSQL->new( { config => $config } )->dbh; +my $dbh = Zonemaster::Backend::DB::PostgreSQL->from_config( $config )->dbh; sub patch_db { diff --git a/share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl new file mode 100644 index 000000000..08b1ba03c --- /dev/null +++ b/share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl @@ -0,0 +1,109 @@ +use strict; +use warnings; +use JSON::PP; +use Encode; + +use DBI qw(:utils); + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB::PostgreSQL; + +my $config = Zonemaster::Backend::Config->load_config(); +if ( $config->DB_engine ne 'PostgreSQL' ) { + die "The configuration file does not contain the PostgreSQL backend"; +} +my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); +my $dbh = $db->dbh; + + +sub patch_db { + # Drop default value for the "hash_id" field + $dbh->do( 'ALTER TABLE test_results ALTER COLUMN hash_id DROP DEFAULT' ); + + # Rename column "params_deterministic_hash" into "fingerprint" + eval { + $dbh->do( 'ALTER TABLE test_results RENAME COLUMN params_deterministic_hash TO fingerprint' ); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + # Update index + eval { + $dbh->do( "DROP INDEX IF EXISTS test_results__params_deterministic_hash" ); + $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); + }; + print( "Error while updating the index: " . $@ ) if ($@); + + # test_start_time and test_end_time default to NULL + eval { + $dbh->do('ALTER TABLE test_results ALTER COLUMN test_start_time SET DEFAULT NULL'); + $dbh->do('ALTER TABLE test_results ALTER COLUMN test_end_time SET DEFAULT NULL'); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + + # Add missing "domain" and "undelegated" columns + eval { + $dbh->do( "ALTER TABLE test_results ADD COLUMN domain VARCHAR(255) NOT NULL DEFAULT ''" ); + $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + # Update index + eval { + $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); + $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); + }; + print( "Error while updating the index: " . $@ ) if ($@); + + # New index + eval { + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__progress_priority_id ON test_results (progress, priority DESC, id) WHERE (progress = 0)' ); + }; + print( "Error while creating the index: " . $@ ) if ($@); + + # Update the "domain" column + $dbh->do( "UPDATE test_results SET domain = (params->>'domain')" ); + # remove default value to "domain" column + $dbh->do( "ALTER TABLE test_results ALTER COLUMN domain DROP DEFAULT" ); + + # Update the "undelegated" column + my $sth1 = $dbh->prepare('SELECT id, params from test_results', undef); + $sth1->execute; + while ( my $row = $sth1->fetchrow_hashref ) { + my $id = $row->{id}; + my $raw_params; + + if (utf8::is_utf8($row->{params}) ) { + $raw_params = decode_json( encode_utf8 ( $row->{params} ) ); + } else { + $raw_params = decode_json( $row->{params} ); + } + + my $ds_info_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{ds_info}}; + my $nameservers_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{nameservers}}; + my $undelegated = $ds_info_values > 0 || $nameservers_values > 0 || 0; + + $dbh->do('UPDATE test_results SET undelegated = ? where id = ?', undef, $undelegated, $id); + } + + # add "username" and "api_key" columns to the "users" table + eval { + $dbh->do( 'ALTER TABLE users ADD COLUMN username VARCHAR(128)' ); + $dbh->do( 'ALTER TABLE users ADD COLUMN api_key VARCHAR(512)' ); + }; + print( "Error while changing DB schema: " . $@ ) if ($@); + + # update the columns + eval { + $dbh->do( "UPDATE users SET username = (user_info->>'username'), api_key = (user_info->>'api_key')" ); + }; + print( "Error while updating the users table: " . $@ ) if ($@); + + # remove the "user_info" column from the "users" table + $dbh->do( "ALTER TABLE users DROP COLUMN IF EXISTS user_info" ); + + # remove the "nb_retries" column from the "test_results" table + $dbh->do( "ALTER TABLE test_results DROP COLUMN IF EXISTS nb_retries" ); +} + +patch_db(); diff --git a/share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl b/share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl new file mode 100644 index 000000000..b1adbd471 --- /dev/null +++ b/share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl @@ -0,0 +1,95 @@ +use strict; +use warnings; +use JSON::PP; + +use DBI qw(:utils); + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB::SQLite; + +my $config = Zonemaster::Backend::Config->load_config(); +if ( $config->DB_engine ne 'SQLite' ) { + die "The configuration file does not contain the SQLite backend"; +} +my $db = Zonemaster::Backend::DB::SQLite->from_config( $config ); +my $dbh = $db->dbh; + + +sub patch_db { + + # since we change the default value for a column, the whole table needs to + # be recreated + # 1. rename the "test_results" table to "test_results_old" + # 2. create the new "test_results" table + # 3. populate it with the values from "test_results_old" + # 4. remove old table and indexes + # 5. recreate the indexes + eval { + $dbh->do('ALTER TABLE test_results RENAME TO test_results_old'); + + # create the table + $db->create_db(); + + # populate it + # - nb_retries is omitted as we remove this column + # - params_deterministic_hash is renamed to fingerprint + $dbh->do(' + INSERT INTO test_results + SELECT id, + hash_id, + domain, + batch_id, + creation_time, + test_start_time, + test_end_time, + priority, + queue, + progress, + params_deterministic_hash, + params, + results, + undelegated + FROM test_results_old + '); + + $dbh->do('DROP TABLE test_results_old'); + + # recreate indexes + $db->create_db(); + }; + print( "Error while updating the 'test_results' table schema: " . $@ ) if ($@); + + # Update the "undelegated" column + my $sth1 = $dbh->prepare('SELECT id, params from test_results', undef); + $sth1->execute; + while ( my $row = $sth1->fetchrow_hashref ) { + my $id = $row->{id}; + my $raw_params = decode_json($row->{params}); + my $ds_info_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{ds_info}}; + my $nameservers_values = scalar grep !/^$/, map { values %$_ } @{$raw_params->{nameservers}}; + my $undelegated = $ds_info_values > 0 || $nameservers_values > 0 || 0; + + $dbh->do('UPDATE test_results SET undelegated = ? where id = ?', undef, $undelegated, $id); + } + + + # in order to properly drop a column, the whole table needs to be recreated + # 1. rename the "users" table to "users_old" + # 2. create the new "users" table + # 3. populate it with the values from "users_old" + # 4. remove old table + eval { + $dbh->do('ALTER TABLE users RENAME TO users_old'); + + # create the table + $db->create_db(); + + # populate it + $dbh->do('INSERT INTO users SELECT id, username, api_key FROM users_old'); + + $dbh->do('DROP TABLE users_old'); + }; + print( "Error while updating the 'users' table schema: " . $@ ) if ($@); +} + +patch_db(); diff --git a/share/patch_db_README.txt b/share/patch_db_README.txt deleted file mode 100644 index 47041d624..000000000 --- a/share/patch_db_README.txt +++ /dev/null @@ -1,2 +0,0 @@ -Find instructions on patching (upgrading) the zonemaster database -on https://github.com/zonemaster/zonemaster-backend/blob/master/README.md#upgrade diff --git a/share/sv.po b/share/sv.po new file mode 100644 index 000000000..9d77b1696 --- /dev/null +++ b/share/sv.po @@ -0,0 +1,76 @@ +msgid "" +msgstr "" +"Project-Id-Version: 1.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-17 08:57+0100\n" +"PO-Revision-Date: 2021-11-17 17:32+0100\n" +"Last-Translator: mats.dufberg@iis.se\n" +"Language-Team: Zonemaster project\n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Domain name required" +msgstr "Domännamn är obligatoriskt" + +msgid "" +"The domain name is not a valid IDNA string and cannot be converted to an A-" +"label" +msgstr "" +"Domännamnet är inte ett giltigt IDN-namn och kan inte konverteras till A-" +"label-format" + +msgid "The domain name contains non-ascii characters and IDNA is not installed" +msgstr "" +"Domännamnet innehåller icke-ASCII-tecken, men stöd för IDN är inte " +"installerat" + +msgid "The domain name character(s) are not supported" +msgstr "Domännamnstecken stöds inte" + +msgid "The domain name or label is too long" +msgstr "Domännamnet eller en domännamnsdel är för långt" + +msgid "Unknown profile" +msgstr "Okänd profil" + +msgid "Invalid IP address" +msgstr "Ogiltig IP-adress" + +msgid "Unkown language string" +msgstr "Okänd språksträng" + +msgid "Language string not unique" +msgstr "Språksträngen är inte unik" + +msgid "Invalid method parameter(s)." +msgstr "Ogiltig metodparameter." + +msgid "Missing property" +msgstr "Värde saknas" + +msgid "" +"Warning: Zonemaster::LDNS not compiled with libidn, cannot handle non-ASCII " +"names correctly." +msgstr "" +"Varning: Zonemaster::LDNS har inte kompilerats med libidn vilket gör att " +"icke-ASCII-strängar inte kan hanteras korrekt" + +msgid "The domain name contains a character or characters not supported" +msgstr "Domännamnet innehåller ett eller flera tecken som inte stöds" + +msgid "Invalid digest format" +msgstr "Ogiltigt format på DS-digest" + +msgid "Algorithm must be a positive integer" +msgstr "Algoritm måste vara ett positivt heltal" + +msgid "Digest type must be a positive integer" +msgstr "Digest-typ måste vara ett positivt heltal" + +msgid "Keytag must be a positive integer" +msgstr "Keytag måste vara ett positivt heltal" + +msgid "Invalid language tag format" +msgstr "Ogiltigt format på språkkoden" diff --git a/share/update-po b/share/update-po new file mode 100755 index 000000000..32027655d --- /dev/null +++ b/share/update-po @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ -z "$1" ] ; then + echo "error: No PO file specified." >&2 + exit 2 +fi +po_file="$1" ; shift + +make update-po POFILES="$po_file" diff --git a/t/config.t b/t/config.t index 949459345..62c410f71 100644 --- a/t/config.t +++ b/t/config.t @@ -4,6 +4,7 @@ use utf8; use Test::More tests => 2; use Test::NoWarnings; +use Test::Differences; use Test::Exception; use Log::Any::Test; # Must come before use Log::Any @@ -16,7 +17,7 @@ subtest 'Everything but NoWarnings' => sub { use_ok( 'Zonemaster::Backend::Config' ); - lives_and { + subtest 'Set values' => sub { my $text = q{ [DB] engine = sqlite @@ -24,12 +25,14 @@ subtest 'Everything but NoWarnings' => sub { [MYSQL] host = mysql-host + port = 3456 user = mysql_user password = mysql_password database = mysql_database [POSTGRESQL] host = postgresql-host + port = 6543 user = postgresql_user password = postgresql_password database = postgresql_database @@ -37,36 +40,58 @@ subtest 'Everything but NoWarnings' => sub { [SQLITE] database_file = /var/db/zonemaster.sqlite + [LANGUAGE] + locale = sv_FI + + [PUBLIC PROFILES] + default = /path/to/default.profile + two = /path/to/two.profile + + [PRIVATE PROFILES] + three = /path/to/three.profile + four = /path/to/four.profile + [ZONEMASTER] max_zonemaster_execution_time = 1200 number_of_processes_for_frontend_testing = 30 number_of_processes_for_batch_testing = 40 lock_on_queue = 1 - maximal_number_of_retries = 2 age_reuse_previous_test = 800 }; my $config = Zonemaster::Backend::Config->parse( $text ); isa_ok $config, 'Zonemaster::Backend::Config', 'parse() return value'; - is $config->DB_engine, 'SQLite', 'set: DB.engine'; - is $config->DB_polling_interval, 1.5, 'set: DB.polling_interval'; - is $config->MYSQL_host, 'mysql-host', 'set: MYSQL.host'; - is $config->MYSQL_user, 'mysql_user', 'set: MYSQL.user'; - is $config->MYSQL_password, 'mysql_password', 'set: MYSQL.password'; - is $config->MYSQL_database, 'mysql_database', 'set: MYSQL.database'; - is $config->POSTGRESQL_host, 'postgresql-host', 'set: POSTGRESQL.host'; - is $config->POSTGRESQL_user, 'postgresql_user', 'set: POSTGRESQL.user'; - is $config->POSTGRESQL_password, 'postgresql_password', 'set: POSTGRESQL.password'; - is $config->POSTGRESQL_database, 'postgresql_database', 'set: POSTGRESQL.database'; - is $config->SQLITE_database_file, '/var/db/zonemaster.sqlite', 'set: SQLITE.database_file'; - is $config->ZONEMASTER_max_zonemaster_execution_time, 1200, 'set: ZONEMASTER.max_zonemaster_execution_time'; - is $config->ZONEMASTER_maximal_number_of_retries, 2, 'set: ZONEMASTER.maximal_number_of_retries'; - is $config->ZONEMASTER_number_of_processes_for_frontend_testing, 30, 'set: ZONEMASTER.number_of_processes_for_frontend_testing'; - is $config->ZONEMASTER_number_of_processes_for_batch_testing, 40, 'set: ZONEMASTER.number_of_processes_for_batch_testing'; - is $config->ZONEMASTER_lock_on_queue, 1, 'set: ZONEMASTER.lock_on_queue'; - is $config->ZONEMASTER_age_reuse_previous_test, 800, 'set: ZONEMASTER.age_reuse_previous_test'; + is $config->DB_engine, 'SQLite', 'set: DB.engine'; + is $config->DB_polling_interval, 1.5, 'set: DB.polling_interval'; + is $config->MYSQL_host, 'mysql-host', 'set: MYSQL.host'; + is $config->MYSQL_port, 3456, 'set: MYSQL.port'; + is $config->MYSQL_user, 'mysql_user', 'set: MYSQL.user'; + is $config->MYSQL_password, 'mysql_password', 'set: MYSQL.password'; + is $config->MYSQL_database, 'mysql_database', 'set: MYSQL.database'; + is $config->POSTGRESQL_host, 'postgresql-host', 'set: POSTGRESQL.host'; + is $config->POSTGRESQL_port, 6543, 'set: POSTGRESQL.port'; + is $config->POSTGRESQL_user, 'postgresql_user', 'set: POSTGRESQL.user'; + is $config->POSTGRESQL_password, 'postgresql_password', 'set: POSTGRESQL.password'; + is $config->POSTGRESQL_database, 'postgresql_database', 'set: POSTGRESQL.database'; + is $config->SQLITE_database_file, '/var/db/zonemaster.sqlite', 'set: SQLITE.database_file'; + eq_or_diff { $config->LANGUAGE_locale }, { sv => { sv_FI => 1 } }, 'set: LANGUAGE.locale'; + eq_or_diff { $config->PUBLIC_PROFILES }, { # + default => '/path/to/default.profile', + two => '/path/to/two.profile' + }, + 'set: PUBLIC PROFILES'; + eq_or_diff { $config->PRIVATE_PROFILES }, { # + three => '/path/to/three.profile', + four => '/path/to/four.profile' + }, + 'set: PRIVATE PROFILES'; + is $config->ZONEMASTER_max_zonemaster_execution_time, 1200, 'set: ZONEMASTER.max_zonemaster_execution_time'; + is $config->ZONEMASTER_number_of_processes_for_frontend_testing, 30, 'set: ZONEMASTER.number_of_processes_for_frontend_testing'; + is $config->ZONEMASTER_number_of_processes_for_batch_testing, 40, 'set: ZONEMASTER.number_of_processes_for_batch_testing'; + is $config->ZONEMASTER_lock_on_queue, 1, 'set: ZONEMASTER.lock_on_queue'; + is $config->ZONEMASTER_age_reuse_previous_test, 800, 'set: ZONEMASTER.age_reuse_previous_test'; }; - lives_and { + subtest 'Default values' => sub { my $text = q{ [DB] engine = SQLite @@ -76,15 +101,20 @@ subtest 'Everything but NoWarnings' => sub { }; my $config = Zonemaster::Backend::Config->parse( $text ); cmp_ok abs( $config->DB_polling_interval - 0.5 ), '<', 0.000001, 'default: DB.polling_interval'; + is $config->MYSQL_port, 3306, 'default: MYSQL.port'; + is $config->POSTGRESQL_port, 5432, 'default: POSTGRESQL.port'; + eq_or_diff { $config->LANGUAGE_locale }, { en => { en_US => 1 } }, 'default: LANGUAGE.locale'; + eq_or_diff { $config->PUBLIC_PROFILES }, { default => undef }, 'default: PUBLIC_PROFILES'; + eq_or_diff { $config->PRIVATE_PROFILES }, {}, 'default: PRIVATE_PROFILES'; is $config->ZONEMASTER_max_zonemaster_execution_time, 600, 'default: ZONEMASTER.max_zonemaster_execution_time'; - is $config->ZONEMASTER_maximal_number_of_retries, 0, 'default: ZONEMASTER.maximal_number_of_retries'; is $config->ZONEMASTER_number_of_processes_for_frontend_testing, 20, 'default: ZONEMASTER.number_of_processes_for_frontend_testing'; is $config->ZONEMASTER_number_of_processes_for_batch_testing, 20, 'default: ZONEMASTER.number_of_processes_for_batch_testing'; is $config->ZONEMASTER_lock_on_queue, 0, 'default: ZONEMASTER.lock_on_queue'; is $config->ZONEMASTER_age_reuse_previous_test, 600, 'default: ZONEMASTER.age_reuse_previous_test'; }; - lives_and { + subtest 'Deprecated values and fallbacks when DB.engine = MySQL' => sub { + $log->clear(); my $text = q{ [DB] engine = MySQL @@ -104,7 +134,8 @@ subtest 'Everything but NoWarnings' => sub { is $config->MYSQL_database, 'db_database', 'fallback: MYSQL.database'; }; - lives_and { + subtest 'Deprecated values and fallbacks when DB.engine = PostgreSQL' => sub { + $log->clear(); my $text = q{ [DB] engine = PostgreSQL @@ -124,7 +155,8 @@ subtest 'Everything but NoWarnings' => sub { is $config->POSTGRESQL_database, 'db_database', 'fallback: POSTGRESQL.database'; }; - lives_and { + subtest 'Deprecated values and fallbacks when DB.engine = SQLite' => sub { + $log->clear(); my $text = q{ [DB] engine = SQLite @@ -141,12 +173,15 @@ subtest 'Everything but NoWarnings' => sub { is $config->SQLITE_database_file, '/var/db/zonemaster.sqlite', 'fallback: SQLITE.database_file'; }; - lives_and { + subtest 'Deprecated values and fallbacks that are unconditional' => sub { + $log->clear(); my $text = q{ [DB] engine = SQLite [SQLITE] database_file = /var/db/zonemaster.sqlite + [LANGUAGE] + locale = [ZONEMASTER] number_of_professes_for_frontend_testing = 21 number_of_professes_for_batch_testing = 22 @@ -154,10 +189,30 @@ subtest 'Everything but NoWarnings' => sub { my $config = Zonemaster::Backend::Config->parse( $text ); $log->contains_ok( qr/deprecated.*ZONEMASTER\.number_of_professes_for_frontend_testing/, 'deprecated: ZONEMASTER.number_of_professes_for_frontend_testing' ); $log->contains_ok( qr/deprecated.*ZONEMASTER\.number_of_professes_for_batch_testing/, 'deprecated: ZONEMASTER.number_of_professes_for_batch_testing' ); + $log->contains_ok( qr/deprecated.*LANGUAGE\.locale/, 'deprecated empty LANGUAGE.locale' ); is $config->ZONEMASTER_number_of_processes_for_frontend_testing, '21', 'fallback: ZONEMASTER.number_of_processes_for_frontend_testing'; is $config->ZONEMASTER_number_of_processes_for_batch_testing, '22', 'fallback: ZONEMASTER.number_of_processes_for_batch_testing'; }; + subtest 'Warnings' => sub { + $log->clear(); + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = localhost + port = 3333 + user = mysql_user + password = mysql_password + database = mysql_database + }; + my $config = Zonemaster::Backend::Config->parse( $text ); + $log->contains_ok( qr/MYSQL\.port.*MYSQL\.host/, 'warning: MYSQL.host is "localhost" and MYSQL.port defined' ); + is $config->MYSQL_host, 'localhost', 'set: MYSQL.host'; + is $config->MYSQL_port, 3333, 'set: MYSQL.port'; + }; + throws_ok { my $text = '{"this":"is","not":"a","valid":"ini","file":"!"}'; Zonemaster::Backend::Config->parse( $text ); @@ -201,6 +256,323 @@ subtest 'Everything but NoWarnings' => sub { } qr/DB\.engine.*Excel/, 'die: Invalid DB.engine value'; + throws_ok { + my $text = q{ + [DB] + engine = SQLite + polling_interval = hourly + + [SQLITE] + databse_file = /var/db/zonemaster.sqlite + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{DB\.polling_interval.*hourly}, 'die: Invalid DB.polling_interval value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = 192.0.2.1:3306 + user = zonemaster_user + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{MYSQL\.host.*192.0.2.1:3306}, 'die: Invalid MYSQL.host value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + user = Robert'); DROP TABLE Students;-- + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{MYSQL\.user.*Robert'\); DROP TABLE Students;--}, 'die: Invalid MYSQL.user value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + user = zonemaster + password = (╯°□°)╯︵ ┻━┻ + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{MYSQL\.password.*\(╯°□°\)╯︵ ┻━┻}, 'die: Invalid MYSQL.password value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + user = zonemaster_user + password = zonemaster_password + database = |)/-\'|'/-\|3/-\$[- + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{MYSQL\.database.*|\)/-\'|'/-\\|3/-\\$[-}, 'die: Invalid MYSQL.database value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = 192.0.2.1:5432 + user = zonemaster_user + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{POSTGRESQL\.host.*192.0.2.1:5432}, 'die: Invalid POSTGRESQL.host value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + user = Robert'); DROP TABLE Students;-- + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{POSTGRESQL\.user.*Robert'\); DROP TABLE Students;--}, 'die: Invalid POSTGRESQL.user value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + user = zonemaster + password = (╯°□°)╯︵ ┻━┻ + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{POSTGRESQL\.password.*\(╯°□°\)╯︵ ┻━┻}, 'die: Invalid POSTGRESQL.password value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + user = zonemaster_user + password = zonemaster_password + database = |)/-\'|'/-\|3/-\$[- + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{POSTGRESQL\.database.*|\)/-\'|'/-\\|3/-\\$[-}, 'die: Invalid POSTGRESQL.database value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = ./relative/path/to/zonemaster.sqlite + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{SQLITE\.database_file.*\./relative/path/to/zonemaster.sqlite}, 'die: Invalid SQLITE.database_file value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + max_zonemaster_execution_time = 0 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.max_zonemaster_execution_time.*0}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + lock_on_queue = -1 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.lock_on_queue.*-1}, 'die: Invalid ZONEMASTER.lock_on_queue value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + number_of_processes_for_frontend_testing = 0 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.number_of_processes_for_frontend_testing.*0}, 'die: Invalid ZONEMASTER.number_of_processes_for_frontend_testing value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + number_of_processes_for_batch_testing = 100000 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.number_of_processes_for_batch_testing.*100000}, 'die: Invalid ZONEMASTER.number_of_processes_for_batch_testing value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + age_reuse_previous_test = 0 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.age_reuse_previous_test.*0}, 'die: Invalid ZONEMASTER.age_reuse_previous_test value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + user = zonemaster_user + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/MYSQL\.host/, 'die: Missing MYSQL.host value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/MYSQL\.user/, 'die: Missing MYSQL.user value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + user = zonemaster_user + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/MYSQL\.password/, 'die: Missing MYSQL.password value'; + + throws_ok { + my $text = q{ + [DB] + engine = MySQL + + [MYSQL] + host = zonemaster-host + user = zonemaster_user + password = zonemaster_password + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/MYSQL\.database/, 'die: Missing MYSQL.database value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + user = zonemaster_user + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/POSTGRESQL\.host/, 'die: Missing POSTGRESQL.host value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + password = zonemaster_password + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/POSTGRESQL\.user/, 'die: Missing POSTGRESQL.user value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + user = zonemaster_user + database = zonemaster_database + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/POSTGRESQL\.password/, 'die: Missing POSTGRESQL.password value'; + + throws_ok { + my $text = q{ + [DB] + engine = PostgreSQL + + [POSTGRESQL] + host = zonemaster-host + user = zonemaster_user + password = zonemaster_password + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/POSTGRESQL\.database/, 'die: Missing POSTGRESQL.database value'; + throws_ok { my $text = q{ [DB] @@ -343,6 +715,150 @@ subtest 'Everything but NoWarnings' => sub { } qr/LANGUAGE\.locale.*en_US/, 'die: Repeated locale_tag in LANGUAGE.locale'; + lives_and { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PUBLIC PROFILES] + DEFAULT = /path/to/my.profile + + [PRIVATE PROFILES] + SECRET = /path/to/my.profile + }; + my $config = Zonemaster::Backend::Config->parse( $text ); + eq_or_diff { $config->PUBLIC_PROFILES }, { default => '/path/to/my.profile' }, 'normalize profile names under PUBLIC PROFILES'; + eq_or_diff { $config->PRIVATE_PROFILES }, { secret => '/path/to/my.profile' }, 'normalize profile names under PRIVATE PROFILES'; + }; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PUBLIC PROFILES] + -invalid-name- = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/PUBLIC PROFILES.*-invalid-name-/, 'die: Invalid profile name in PUBLIC PROFILES'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PRIVATE PROFILES] + -invalid-name- = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/PRIVATE PROFILES.*-invalid-name-/, 'die: Invalid profile name in PRIVATE PROFILES'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PUBLIC PROFILES] + valid-name = relative/path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/absolute.*valid-name/, 'die: Invalid absolute path in PUBLIC PROFILES'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PRIVATE PROFILES] + valid-name = relative/path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/absolute.*valid-name/, 'die: Invalid absolute path in PRIVATE PROFILES'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PUBLIC PROFILES] + valid-name = /path/to/my.profile + valid-name = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/unique.*valid-name/, 'die: Repeated profile name in PUBLIC PROFILES section'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PRIVATE PROFILES] + valid-name = /path/to/my.profile + valid-name = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/unique.*valid-name/, 'die: Repeated profile name in PRIVATE PROFILES section'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PUBLIC PROFILES] + pub-and-priv = /path/to/my.profile + + [PRIVATE PROFILES] + pub-and-priv = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/unique.*pub-and-priv/, 'die: Repeated profile name across sections'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [SQLITE] + database_file = /var/db/zonemaster.sqlite + + [PRIVATE PROFILES] + default = /path/to/my.profile + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr/PRIVATE PROFILES.*default/, 'die: Default profile in PRIVATE PROFILES'; + { my $path = catfile( dirname( $0 ), '..', 'share', 'backend_config.ini' ); my $text = read_file( $path ); diff --git a/t/db.t b/t/db.t new file mode 100644 index 000000000..c3c460632 --- /dev/null +++ b/t/db.t @@ -0,0 +1,172 @@ +use strict; +use warnings; + +use Test::More; # see done_testing() + +use_ok( 'Zonemaster::Backend::DB' ); +use_ok( 'JSON::PP' ); + +sub encode_and_fingerprint { + my $params = shift; + + my $self = "Zonemaster::Backend::DB"; + my $encoded_params = $self->encode_params( $params ); + my $fingerprint = $self->generate_fingerprint( $params ); + + return ( $encoded_params, $fingerprint ); +} + +subtest 'encoding and fingerprint' => sub { + + subtest 'missing properties' => sub { + my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default"}'; + + my %params = ( domain => "example.com" ); + + my ( $encoded_params, $fingerprint ) = encode_and_fingerprint( \%params ); + is $encoded_params, $expected_encoded_params, 'domain only: the encoded strings should match'; + #diag ($fingerprint); + + $params{ipv4} = JSON::PP->true; + my ( $encoded_params_ipv4, $fingerprint_ipv4 ) = encode_and_fingerprint( \%params ); + is $encoded_params_ipv4, $expected_encoded_params, 'add ipv4: the encoded strings should match'; + is $fingerprint_ipv4, $fingerprint, 'fingerprints should match'; + }; + + subtest 'array properties' => sub { + subtest 'ds_info' => sub { + my %params1 = ( + domain => "example.com", + ds_info => [{ + algorithm => 8, + keytag => 11627, + digtype => 2, + digest => "a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448" + }] + ); + my %params2 = ( + ds_info => [{ + digtype => 2, + algorithm => 8, + keytag => 11627, + digest => "a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448" + }], + domain => "example.com" + ); + my ( $encoded_params1, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + is $fingerprint1, $fingerprint2, 'ds_info same fingerprint'; + is $encoded_params1, $encoded_params2, 'ds_info same encoded string'; + }; + + subtest 'nameservers order' => sub { + my %params1 = ( + domain => "example.com", + nameservers => [ + { ns => "ns2.nic.fr", ip => "192.134.4.1" }, + { ns => "ns1.nic.fr" }, + { ip => "192.0.2.1", ns => "ns3.nic.fr"} + ] + ); + my %params2 = ( + nameservers => [ + { ns => "ns3.nic.fr", ip => "192.0.2.1" }, + { ns => "ns1.nic.fr" }, + { ip => "192.134.4.1", ns => "ns2.nic.fr"} + ], + domain => "example.com" + ); + my %params3 = ( + domain => "example.com", + nameservers => [ + { ip => "", ns => "ns1.nic.fr" }, + { ns => "ns3.nic.FR", ip => "192.0.2.1" }, + { ns => "ns2.nic.fr", ip => "192.134.4.1" } + ] + ); + my %params4 = ( + domain => "example.com", + nameservers => [ + { ip => "192.134.4.1", ns => "nS2.Nic.FR"}, + { ns => "Ns1.nIC.fR", ip => "" }, + { ns => "ns3.nic.fr", ip => "192.0.2.1" } + ] + ); + + my ( $encoded_params1, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + my ( $encoded_params3, $fingerprint3 ) = encode_and_fingerprint( \%params3 ); + my ( $encoded_params4, $fingerprint4 ) = encode_and_fingerprint( \%params4 ); + + is $fingerprint1, $fingerprint2, 'nameservers: same fingerprint'; + is $encoded_params1, $encoded_params2, 'nameservers: same encoded string'; + + is $fingerprint1, $fingerprint3, 'nameservers: same fingerprint (empty ip)'; + is $encoded_params1, $encoded_params3, 'nameservers: same encoded string (empty ip)'; + + is $fingerprint1, $fingerprint4, 'nameservers: same fingerprint (ignore nameservers\' ns case)'; + is $encoded_params1, $encoded_params4, 'nameservers: same encoded string (ignore nameservers\' ns case)'; + }; + }; + + subtest 'should be case insensitive' => sub { + my %params1 = ( domain => "example.com" ); + my %params2 = ( domain => "eXamPLe.COm" ); + + my ( $encoded_params1, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + is $fingerprint1, $fingerprint2, 'same fingerprint'; + is $encoded_params1, $encoded_params2, 'same encoded string'; + }; + + subtest 'garbage properties set' => sub { + my $expected_encoded_params = '{"client":"GUI v3.3.0","domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default"}'; + my %params1 = ( + domain => "example.com", + ); + my %params2 = ( + domain => "example.com", + client => "GUI v3.3.0" + ); + my ( $encoded_params1, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + + is $fingerprint1, $fingerprint2, 'leave out garbage property in fingerprint computation...'; + is $encoded_params2, $expected_encoded_params, '...but keep it in the encoded string'; + }; + + subtest 'should have different fingerprints' => sub { + subtest 'different profiles' => sub { + my %params1 = ( + domain => "example.com", + profile => "profile_1" + ); + my %params2 = ( + domain => "example.com", + profile => "profile_2" + ); + my ( undef, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( undef, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + + isnt $fingerprint1, $fingerprint2, 'different profiles, different fingerprints'; + }; + subtest 'different IP protocols' => sub { + my %params1 = ( + domain => "example.com", + ipv4 => "true", + ipv6 => "false" + ); + my %params2 = ( + domain => "example.com", + ipv4 => "false", + ipv6 => "true" + ); + my ( undef, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( undef, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + + isnt $fingerprint1, $fingerprint2, 'different IP protocols, different fingerprints'; + }; + } +}; + +done_testing(); diff --git a/t/parameters_validation.t b/t/parameters_validation.t new file mode 100644 index 000000000..862b08c96 --- /dev/null +++ b/t/parameters_validation.t @@ -0,0 +1,197 @@ +use strict; +use warnings; +use 5.14.2; +use utf8; + +use Test::More tests => 4; +use Test::NoWarnings; + +use File::Temp qw[tempdir]; +use Zonemaster::Backend::Config; +use Zonemaster::Backend::RPCAPI; +use JSON::Validator::Joi "joi"; +use JSON::PP; + + +my $tempdir = tempdir( CLEANUP => 1 ); + +my $config = Zonemaster::Backend::Config->parse( <new( + { + dbtype => $config->DB_engine, + config => $config, + } +); + +sub test_validation { + my ( $method_name, $method_schema, $sub_validator, $test_cases ) = @_; + + subtest "Method $method_name" => sub { + for my $test_case (@$test_cases) { + subtest 'Test case: ' . $test_case->{name} => sub { + my @res = $rpcapi->validate_params( $method_schema, $sub_validator, $test_case->{input}); + is_deeply(\@res, $test_case->{output}, 'Matched validation output' ) or diag( encode_json \@res); + }; + } + }; +} + +subtest 'Test JSON schema' => sub { + my $test_joi_schema = joi->new->object->strict->props( + hostname => joi->new->string->max(10)->required + ); + + my $test_raw_schema = { + type => 'object', + additionalProperties => 0, + required => [ 'hostname' ], + properties => { + hostname => { + type => 'string', + maxLength => 10 + } + } + }; + + my $test_cases = [ + { + name => 'Empty request', + input => {}, + output => [{ + message => 'Missing property', + path => '/hostname' + }] + }, + { + name => 'Correct request', + input => { + hostname => 'example' + }, + output => [] + }, + { + name => 'Bad request', + input => { + hostname => 'example.toolong' + }, + output => [{ + message => 'String is too long: 15/10.', + path => '/hostname' + }] + } + ]; + + test_validation 'test_joi', $test_joi_schema, undef, $test_cases; + test_validation 'test_raw', $test_raw_schema, undef, $test_cases; +}; + +subtest 'Test custom error message' => sub { + my $test_custom_error_schema = { + type => 'object', + additionalProperties => 0, + required => [ 'hostname' ], + additionalProperties => 0, + properties => { + hostname => { + type => 'string', + 'x-error-message' => 'Bad hostname, should be a string less than 10 characters long', + maxLength => 10 + }, + nameservers => { + type => 'array', + items => { + type => 'object', + required => [ 'ip' ], + additionalProperties => 0, + properties => { + ip => { + type => 'string', + 'x-error-message' => 'Bad IP address', + pattern => '^[a-f0-9\.:]+$' + } + } + } + } + } + }; + + my $test_cases = [ + { + name => 'Bad input', + input => { + hostname => 'This is a bad input', + nameservers => [ + { ip => 'Very bad indeed'}, + { ip => '10.10.10.10' }, + { ip => 'But not the previous property' } + ] + }, + output => [ + { + path => '/hostname', + message => 'Bad hostname, should be a string less than 10 characters long', + }, + { + path => '/nameservers/0/ip', + message => 'Bad IP address', + }, + { + path => '/nameservers/2/ip', + message => 'Bad IP address', + } + ] + } + ]; + + test_validation 'test_custom_error', $test_custom_error_schema, undef, $test_cases; +}; + +subtest 'Test extra validators' => sub { + my $test_extra_validator_sub = sub { + my ($self, $input) = @_; + my @errors; + if ( $input->{answer} != 42 ) { + push @errors, { path => '/answer', message => 'Not the expected answer' }; + } + + return @errors; + }; + + my $test_extra_validator_schema = { + type => 'object', + properties => { + answer => { + type => 'number', + } + } + }; + + my $test_cases = [ + { + name => 'Input ok', + input => { + answer => 42, + }, + output => [] + }, + { + name => 'Bad input', + input => { + answer => 0, + }, + output => [{ + path => '/answer', + message => 'Not the expected answer' + }] + } + ]; + + test_validation 'test_extra_validator', $test_extra_validator_schema, $test_extra_validator_sub, $test_cases; +}; diff --git a/t/test01.data b/t/test01.data index 07ca9370b..e2f9c2b3c 100644 --- a/t/test01.data +++ b/t/test01.data @@ -1,126 +1,124 @@ -f.in-addr-servers.arpa 193.0.9.1 {"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":9,"timestamp":1552922479.77592,"answerfrom":"193.0.9.1","data":"5/2AAAABAAAABgAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBQAAgABAAFRgAAMAXIEYXJpbgNuZXQAwBQAAgABAAFRgAAEAXXAOMAUAAIAAQABUYAABAF4wDjAFAACAAEAAVGAAAQBecA4wBQAAgABAAFRgAAEAXrAOMAUAAIAAQABUYAAFARhcmluB2F1dGhkbnMEcmlwZcA9"}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"JMSAAAABAAAABgAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAVAAIAAQABUYAADAFyBGFyaW4DbmV0AMAVAAIAAQABUYAABAF1wDnAFQACAAEAAVGAAAQBeMA5wBUAAgABAAFRgAAEAXnAOcAVAAIAAQABUYAABAF6wDnAFQACAAEAAVGAABQEYXJpbgdhdXRoZG5zBHJpcGXAPg==","answerfrom":"193.0.9.1","querytime":9,"timestamp":1552922481.20842}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":9,"timestamp":1552922480.35556,"answerfrom":"193.0.9.1","data":"zDCAAAABAAAABgAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAEwACAAEAAVGAAAwBcgRhcmluA25ldADAEwACAAEAAVGAAAQBdcA3wBMAAgABAAFRgAAEAXjAN8ATAAIAAQABUYAABAF5wDfAEwACAAEAAVGAAAQBesA3wBMAAgABAAFRgAAUBGFyaW4HYXV0aGRucwRyaXBlwDw="}}}} -f.in-addr-servers.arpa 2001:067c:00e0:0000:0000:0000:0000:0001 {"xA39FCnoMsmugxaD74lRSw":null,"txj88I7p+6ryfLcyf3NH/g":null,"7mmtEYchsO9PZT5B8N6/Pg":null} -i.root-servers.net 192.36.148.17 {} -i.root-servers.net 2001:07fe:0000:0000:0000:0000:0000:0053 {} +e.gtld-servers.net 192.12.94.30 {} +b.root-servers.net 199.9.14.201 {} +b.root-servers.net 2001:0500:0200:0000:0000:0000:0000:000b {} +m.gtld-servers.net 192.55.83.30 {"sNttT2q5QOS0nfEEkjvGLQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875444.64093,"querytime":36,"answerfrom":"192.55.83.30","data":"k9eEAAABAAEADQAKA25ldAAABgABwAwABgABAAADhAA9AWEMZ3RsZC1zZXJ2ZXJzwAwFbnN0bGQMdmVyaXNpZ24tZ3JzA2NvbQBg+CYjAAAHCAAAA4QACTqAAAFRgMAMAAIAAQACowAABAFkwCPADAACAAEAAqMAAAQBYsAjwAwAAgABAAKjAAAEAWjAI8AMAAIAAQACowAABAFtwCPADAACAAEAAqMAAAQBZsAjwAwAAgABAAKjAAAEAWzAI8AMAAIAAQACowAABAFlwCPADAACAAEAAqMAAAQBacAjwAwAAgABAAKjAAAEAWrAI8AMAAIAAQACowAAAsAhwAwAAgABAAKjAAAEAWPAI8AMAAIAAQACowAABAFrwCPADAACAAEAAqMAAAQBZ8AjwGoAAQABAAKjAAAEwB9QHsBqABwAAQACowAAECABBQCFbgAAAAAAAAAAADDAegABAAEAAqMAAATAIQ4ewHoAHAABAAKjAAAQIAEFAyMdAAAAAAAAAAIAMMCKAAEAAQACowAABMA2cB7AigAcAAEAAqMAABAgAQUCCMwAAAAAAAAAAAAwwJoAAQABAAKjAAAEwDdTHsCaABwAAQACowAAECABBQGx+QAAAAAAAAAAADDAqgABAAEAAqMAAATAIzMewLoAAQABAAKjAAAEwCmiHg=="}}},"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875444.9179,"data":"13OAAAABAAAABAAIA2F2NAVuc3RsZANjb20AAAEAAcAQAAIAAQACowAABgNhdjHAEMAQAAIAAQACowAABgNhdjLAEMAQAAIAAQACowAABgNhdjPAEMAQAAIAAQACowAAAsAMwCsAAQABAAKjAAAEwCqxHsArABwAAQACowAAECABBQABJAAAAAAAAAAAADDAPQABAAEAAqMAAATAKrIewD0AHAABAAKjAAAQIAEFAAElAAAAAAAAAAAAMMBPAAEAAQACowAABMBShR7ATwAcAAEAAqMAABAgAQUAASYAAAAAAAAAAAAwwAwAAQABAAKjAAAEwFKGHsAMABwAAQACowAAECABBQABJwAAAAAAAAAAADA=","answerfrom":"192.55.83.30"}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"4t6AAAABAAAAAwAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAaAAIAAQACowAADANuczIDbmljAmZyAMAaAAIAAQACowAACwJucwNuaWMCc2UAwBoAAgABAAKjAAAGA25zM8BV","answerfrom":"192.55.83.30","querytime":36,"timestamp":1626875445.87544}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875444.1256,"data":"uLOAAAABAAAAAwAACWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwBYAAgABAAKjAAAMA25zMgNuaWMCZnIAwBYAAgABAAKjAAALAm5zA25pYwJzZQDAFgACAAEAAqMAAAYDbnMzwFE=","answerfrom":"192.55.83.30"}}},"UBMcusig6w3synYwbp7omA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875440.87095,"data":"UqSAAAABAAAABAAIBXJpcm5zBGFyaW4DbmV0AAABAAHAEgACAAEAAqMAAAYDbnMxwBLAEgACAAEAAqMAAAYDbnMywBLAEgACAAEAAqMAAAQBdcASwBIAAgABAAKjAAAGA25zM8ASwCwAAQABAAKjAAAEx9QAbMAsABwAAQACowAAECABBQAAEwAAAAAAAAAAAQjAPgABAAEAAqMAAATHRwBswD4AHAABAAKjAAAQIAEFAAAxAAAAAAAAAAABCMBQABwAAQACowAAECABBQAAFGBQAK0AAAAAAAHAUAABAAEAAqMAAATMPdgywGAAAQABAAKjAAAExwUabMBgABwAAQACowAAECABBQAAqQAAAAAAAAAAAQg=","answerfrom":"192.55.83.30"}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875441.6537,"data":"ov6AAAABAAAABAAIAXoEYXJpbgNuZXQAABwAAcAOAAIAAQACowAABgNuczHADsAOAAIAAQACowAABgNuczLADsAOAAIAAQACowAABAF1wA7ADgACAAEAAqMAAAYDbnMzwA7AKAABAAEAAqMAAATH1ABswCgAHAABAAKjAAAQIAEFAAATAAAAAAAAAAABCMA6AAEAAQACowAABMdHAGzAOgAcAAEAAqMAABAgAQUAADEAAAAAAAAAAAEIwEwAHAABAAKjAAAQIAEFAAAUYFAArQAAAAAAAcBMAAEAAQACowAABMw92DLAXAABAAEAAqMAAATHBRpswFwAHAABAAKjAAAQIAEFAACpAAAAAAAAAAABCA==","answerfrom":"192.55.83.30"}}},"lasV0cL3Q8ch+RpJm5zPYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"5qGAAAABAAAABAAIBXJpcm5zBGFyaW4DbmV0AAAcAAHAEgACAAEAAqMAAAYDbnMxwBLAEgACAAEAAqMAAAYDbnMywBLAEgACAAEAAqMAAAQBdcASwBIAAgABAAKjAAAGA25zM8ASwCwAAQABAAKjAAAEx9QAbMAsABwAAQACowAAECABBQAAEwAAAAAAAAAAAQjAPgABAAEAAqMAAATHRwBswD4AHAABAAKjAAAQIAEFAAAxAAAAAAAAAAABCMBQABwAAQACowAAECABBQAAFGBQAK0AAAAAAAHAUAABAAEAAqMAAATMPdgywGAAAQABAAKjAAAExwUabMBgABwAAQACowAAECABBQAAqQAAAAAAAAAAAQg=","answerfrom":"192.55.83.30","querytime":35,"timestamp":1626875441.03381}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.55.83.30","data":"iX2AAAABAAAABAAIAXoEYXJpbgNuZXQAAAEAAcAOAAIAAQACowAABgNuczHADsAOAAIAAQACowAABgNuczLADsAOAAIAAQACowAABAF1wA7ADgACAAEAAqMAAAYDbnMzwA7AKAABAAEAAqMAAATH1ABswCgAHAABAAKjAAAQIAEFAAATAAAAAAAAAAABCMA6AAEAAQACowAABMdHAGzAOgAcAAEAAqMAABAgAQUAADEAAAAAAAAAAAEIwEwAHAABAAKjAAAQIAEFAAAUYFAArQAAAAAAAcBMAAEAAQACowAABMw92DLAXAABAAEAAqMAAATHBRpswFwAHAABAAKjAAAQIAEFAACpAAAAAAAAAAABCA==","timestamp":1626875441.49903,"querytime":35}}},"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875444.45469,"querytime":35,"answerfrom":"192.55.83.30","data":"ZaqAAAABAAAAAwAACnpvbmVtYXN0ZXIDbmV0AAAGAAHADAACAAEAAqMAAAwDbnMyA25pYwJmcgDADAACAAEAAqMAAAsCbnMDbmljAnNlAMAMAAIAAQACowAABgNuczPARw=="}}},"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.55.83.30","data":"S8GAAAABAAAABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwA4AAgABAAKjAAAPA2F2MQVuc3RsZANjb20AwA4AAgABAAKjAAAGA2F2MsA0wA4AAgABAAKjAAAGA2F2M8A0wA4AAgABAAKjAAAGA2F2NMA0","timestamp":1626875445.21098,"querytime":35}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875445.06592,"data":"jO6AAAABAAAABAAIA2F2NAVuc3RsZANjb20AABwAAcAQAAIAAQACowAABgNhdjHAEMAQAAIAAQACowAABgNhdjLAEMAQAAIAAQACowAABgNhdjPAEMAQAAIAAQACowAAAsAMwCsAAQABAAKjAAAEwCqxHsArABwAAQACowAAECABBQABJAAAAAAAAAAAADDAPQABAAEAAqMAAATAKrIewD0AHAABAAKjAAAQIAEFAAElAAAAAAAAAAAAMMBPAAEAAQACowAABMBShR7ATwAcAAEAAqMAABAgAQUAASYAAAAAAAAAAAAwwAwAAQABAAKjAAAEwFKGHsAMABwAAQACowAAECABBQABJwAAAAAAAAAAADA=","answerfrom":"192.55.83.30"}}},"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"ea6AAAABAAAABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwA4AAgABAAKjAAAPA2F2MQVuc3RsZANjb20AwA4AAgABAAKjAAAGA2F2MsA0wA4AAgABAAKjAAAGA2F2M8A0wA4AAgABAAKjAAAGA2F2NMA0","answerfrom":"192.55.83.30","querytime":36,"timestamp":1626875444.80195}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.55.83.30","data":"nRqAAAABAAAAAwAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAaAAIAAQACowAADANuczIDbmljAmZyAMAaAAIAAQACowAACwJucwNuaWMCc2UAwBoAAgABAAKjAAAGA25zM8BV","timestamp":1626875446.05073,"querytime":36}}}} +a.root-servers.net 2001:0503:ba3e:0000:0000:0000:0002:0030 {} +a.root-servers.net 198.41.0.4 {"anMXpIkCCaKzuyWsrPPtLw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"198.41.0.4","data":"nreAAAABAAAABAAIAmZyAAACAAHADAACAAEAAqMAAAwBZQNleHQDbmljwAzADAACAAEAAqMAAAQBZsAiwAwAAgABAAKjAAAEAWfAIsAMAAIAAQACowAABAFkwCbAIAABAAEAAqMAAATBsJAWwCAAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsA4AAEAAQACowAABMKSai7AOAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEgAAQABAAKjAAAEwgAkAcBIABwAAQACowAAECABBngATAAAAAAAAAAAAAHAWAABAAEAAqMAAATCAAkBwFgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAQ==","timestamp":1626875437.2234,"querytime":26}}},"Ox0c7Ezn+YV8Irn2871Liw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"198.41.0.4","data":"ooqAAAABAAAADQAPA25ldAAAAgABwAwAAgABAAKjAAARAWEMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBYsAjwAwAAgABAAKjAAAEAWPAI8AMAAIAAQACowAABAFkwCPADAACAAEAAqMAAAQBZcAjwAwAAgABAAKjAAAEAWbAI8AMAAIAAQACowAABAFnwCPADAACAAEAAqMAAAQBaMAjwAwAAgABAAKjAAAEAWnAI8AMAAIAAQACowAABAFqwCPADAACAAEAAqMAAAQBa8AjwAwAAgABAAKjAAAEAWzAI8AMAAIAAQACowAABAFtwCPAIQABAAEAAqMAAATABQYewD4AAQABAAKjAAAEwCEOHsBOAAEAAQACowAABMAaXB7AXgABAAEAAqMAAATAH1AewG4AAQABAAKjAAAEwAxeHsB+AAEAAQACowAABMAjMx7AjgABAAEAAqMAAATAKl0ewJ4AAQABAAKjAAAEwDZwHsCuAAEAAQACowAABMArrB7AvgABAAEAAqMAAATAME8ewM4AAQABAAKjAAAEwDSyHsDeAAEAAQACowAABMApoh7A7gABAAEAAqMAAATAN1MewCEAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMMA+ABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","timestamp":1626875444.69765,"querytime":27}}}} k.root-servers.net 193.0.14.129 {} k.root-servers.net 2001:07fd:0000:0000:0000:0000:0000:0001 {} -ns1.nic.fr 2001:067c:2218:0002:0000:0000:0004:0001 {"u2IVdHOj+hP1WgRzMMG0Hg":null,"CFtsJvzdNZr5xrBiWkborQ":null,"Y/FppjiF5p7LpFx+5iKNTQ":null,"F8IuiH+hJkzDv9m0t6ioyA":null,"cgyeMZwmsWADV4zvkMbarg":null,"nys7lrpg8L2EaHBxy2MfKg":null,"G5mY7b4q1l50Mf6YfNGUtg":null,"2/fg0ozhiGSsmWtVHzHJow":null,"TJ/AJOD5oSy8le0E7lFXTg":null,"4YeoSYuQtm/OKn1ycfuP2Q":null,"pOu0bQGV1Z/qItEzagW7FA":null,"zueeZkdHfWptXkiyz6gmOA":null,"KsaIpCVhpeMsuVQal+DZhQ":null,"UzfnoYKACE71u6V/QQ17nw":null,"deQZZWplsy7gFo3gqzBFcg":null,"auoc0YjagPcFkyMHmxRk1g":null} -ns1.nic.fr 192.134.4.1 {"Y/FppjiF5p7LpFx+5iKNTQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"1QOEAAABAAEAAQAAA3d3VwVhRm5JYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","timestamp":1552922487.42673,"querytime":1,"answerfrom":"192.134.4.1"}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7rqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","querytime":1,"timestamp":1552922486.95384,"answerfrom":"192.134.4.1"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1552922486.66774,"answerfrom":"192.134.4.1","data":"9ZKEAAABAAEABQAIA25zMQNuaWMCZnIAABwAAcAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAEAACAAEAAqMAAAYDbnMywBDAEAACAAEAAqMAAALADMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAYDbnM2wGjAZAABAAEAAqMAAATBM9ANwAwAAQABAAKjAAAEwIYEAcBEAAEAAQACowAABMBdAATAegABAAEAAqMAAATAhgAxwIwAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBmAwBQABAAAAAAABAALAegAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwIwAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"MayEAAABAAIABAAIBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAAEcAAYIAgACowBctpmtXI7zNpV5BWFmbmljAmZyAF6LDoTpUAz47+zOXKAlvTGFubGj0aY/kCo/hWXID+P/HyaIpHu2wJ1AS9JS+8szFUc1xgXDYM9zWD74UWs7zQ2KDMZEo6cl4BrvmdCpiv78Ibbfq3eSuhrlo2eZs/IaogFx4CD2jWqODJyk1butmuXwXJognXi6slKUWf9Nv0u7wAktqr86wFgrF37LtjHrXWo6hVvRQpu/wRVkn4u5QFTuHaNowfzf4vBOgrLCNnMStJbkpNaxc+ZQ9Nz0JmtzvfiwmwrWLamRtybKXl6wZTSaiAR7h2P1LdM7SC45c1GNOj3dBbUZos4/oVvgE8zd3B3a9OzT1e2uuMh9UB7R8yzADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAARwAAggCAAKjAFyzsWFci6d2lXkFYWZuaWMCZnIAJ1sp9VayWIT0DZ5sxDsENjHeQjPB7dwDb2cK+uOhZiGuQb7QFdY3Odjp5tuBwlpGNy28Kh05ReK0SCXzGuXX7jF4V5xqhBVtla8fXkSrF1t/rAMfS+uXGj41+jfFeDkryYbzMen0TVHrEuTLN9FjNceJUIoka6HocMdp83ZOb6NDtO5e5ulC8KPUSsqVOXDRCjqKnKDvEFXQ5Hpl8zvbgcl7gGWZsD/GOChWvjiQr5zvBzofZtTne6QC7ryt28ud7iiFweQVbpkLb2Tm2Cc5D7HbDwS5H9J7/FKfYOGOQrJr9JTECyZ4het2DEq3OUIcTqrhvQYGCBNwvRPCwgqQJ8GvAAEAAQACowAABMCGBAHBnQABAAEAAqMAAATAXQAEwYsAAQABAAKjAAAEwIYAMcGvABwAAQACowAAECABBnwiGAACAAAAAAAEAAHBnQAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwYsAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcGvAC4AAQACowABGgABCAMAAqMAXLJSp1yKs/+VeQNuaWMCZnIAJDUwzKZjE5jgX0eJlvWuzoKfX+cZ9MoYMWmzEwyWHPyRaX105nFG64W7ERDmTcFNx7V5L2lcDRRrcIwKPEYfWQDdkjvndKajZoELm7oJzcdzEe1JVeA6rc+kzxgCpSWvDc5sM/eYcfjkJ7B5XeZUr0VdunJDPD0pUGaLAAHMbt8aa9ly3HymaxC+PA8uJaFyinyeiGGKnyrYDgx7Kaf+RuSNFEuvEaAP9Xw93LpReYafGAGU+4vB4WXUPtzcBjUip+GYNQuo+Ekyg5eIgjreAsWbxSvvlhX0umsQZYofbXY99jHLTf6ND3LRMbe7n4cju02I4Wf0Wi/72TO+x4DN9gAAKQWYAACAAAAA","timestamp":1552922486.40763,"querytime":2,"answerfrom":"192.134.4.1"}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922487.2317,"data":"3ZGEAAABAAEAAwAGBWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgAMAAAAAAAAAAlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczLARsBCAAEAAQACowAABMCGBAHAagABAAEAAqMAAATAXQAEwFgAAQABAAKjAAAEwIYAMcBCABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAagAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwFgAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAQ=="}}},"P5BT4xLKPc0cvgR8IR22ww":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922501.4485,"querytime":1,"answerfrom":"192.134.4.1","data":"6JeEAAABAAEAAQAAA1dXdwVhZm5pQwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"amIcOxD+RSRHr9RBm4BZsw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"4RiEAAABAAIAAwAMBWFmbmljAmZyAAAPAAHADAAPAAEAAqMAAAwACgNteDUDbmljwBLADAAPAAEAAqMAAAgAFANteDTALMAMAAIAAQACowAABgNuczHALMAMAAIAAQACowAABgNuczLALMAMAAIAAQACowAABgNuczPALMAoAAEAAQACowAABMCGBA3AQAABAAEAAqMAAATAhgQMwFIAAQABAAKjAAAEwIYEAcBkAAEAAQACowAABMBdAATAdgABAAEAAqMAAATAhgAxwCgAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAE8BAABwAAQACowAAECABBnwiGAACAAAAAAAEABLAUgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGQAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsB2ABwAAQACowAAECABBmAwBgABAAAAAAABAAEDXzI1BF90Y3DAKAA0AAEAAqMAACMDAAGM7Kc+DWl7w5GB1/6VM8dXADe3vPfo5Nt8iabSexKN7gNfMjUEX3RjcMBAADQAAQACowAAIwMAAYzspz4NaXvDkYHX/pUzx1cAN7e89+jk23yJptJ7Eo3u","querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922488.12278}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1552922487.00535,"answerfrom":"192.134.4.1","data":"yB2EAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1552922477.79371,"answerfrom":"192.134.4.1","data":"pUiEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwIcAAQABAAKjAAAEwIYEAcB1AAEAAQACowAABMBdAATAYwABAAEAAqMAAATAhgAxwIcAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcB1ABwAAQACowAAECABBmAwBQABAAAAAAABAALAYwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922486.70734,"data":"OUOEAAABAAEABQAIA25zMwNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBgABAAAAAAABAAHAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnM2wFrAEAACAAEAAqMAAAYDbnMywBDAVgABAAEAAqMAAATBM9ANwEQAAQABAAKjAAAEwIYEAcCMAAEAAQACowAABMBdAATADAABAAEAAqMAAATAhgAxwHoAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAjAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHoAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"deQZZWplsy7gFo3gqzBFcg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"uAeEAAABAAEAAQAAA1dXVwVhZk5JQwJmcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":1,"answerfrom":"192.134.4.1","timestamp":1552922487.53232}}},"KNmqNpIE1P0AaV9ISH94Iw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"p5GEAwABAAAACAABC3h4LS1leGFtcGxlBWFmbmljAmZyAAABAAHAGAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8AeCmhvc3RtYXN0ZXLAPHhX+vgAABwgAAAHCAAk6gAAABUYwBgALgABAAAVGAEcAAYIAgACowBctpmtXI7zNpV5BWFmbmljAmZyAF6LDoTpUAz47+zOXKAlvTGFubGj0aY/kCo/hWXID+P/HyaIpHu2wJ1AS9JS+8szFUc1xgXDYM9zWD74UWs7zQ2KDMZEo6cl4BrvmdCpiv78Ibbfq3eSuhrlo2eZs/IaogFx4CD2jWqODJyk1butmuXwXJognXi6slKUWf9Nv0u7wAktqr86wFgrF37LtjHrXWo6hVvRQpu/wRVkn4u5QFTuHaNowfzf4vBOgrLCNnMStJbkpNaxc+ZQ9Nz0JmtzvfiwmwrWLamRtybKXl6wZTSaiAR7h2P1LdM7SC45c1GNOj3dBbUZos4/oVvgE8zd3B3a9OzT1e2uuMh9UB7R8ywgdWM1Z2dxcWk0NDhnYXEzbjlmcnBkdjltMmtyaGxyNXDAGAAyAAEAABUYACsBAQABCI5sg30w/SoyFPahDka4VxdPQuZTh70jI+a5t2igAAdiAYAIAAKQwYsALgABAAAVGAEcADIIAwAAFRhcpycmXH8FFpV5BWFmbmljAmZyAEUnegDMeXEAtaEtFeRCSmuNwNLoQF9JBtB53323us4ySi+MTOCjYFQXmr8xyH82IYdgXd9xtLLLsyVvKByzh09rhSa1j+YgOHIC9kar8NJvCCE0HQvbBikexqe5+sqE54rBJKXQrVki7CyFExVGZ9JoQu9AKOVlzhNj2EcgLMKXgnYe+xbqUNIpwMElIHpmaHuwHoem1bGx4rCiO/gKUH79Mxo0K7s81LoHC+pwm+RR+YaiqBFK/ToVqM/BgLMnfPimrc+5i8BanV/UpG0mhWoh0Nu5HFX84Dtcg4pgqBjuVMMLTtA7IzpzH7/ikglYAA8zFbMKbnM3nCrFynJy/oAgdDhzZjJkZDIwYzYycDVtaDdjYWVwcmg4b2RlOW5kYmPAGAAyAAEAABUYACoBAQABCI5sg30w/SoyFPMLCGtSIREFaHdL95b9NhU3Guy5AAYEAAAAAALDCwAuAAEAABUYARwAMggDAAAVGFymo05cfpSWlXkFYWZuaWMCZnIAikUongjovNro7R2jmzEioQnV8EQViak2+xnatU2n1ae8v21Rbw71sCdEGQN3VJ4lKuigzG50co+GKzjkdNuHtoK1MxpXl4HaJ+N7tAMQHeQaBgMyZG7BctOJq2GFv8MTlnIaH2eixwosnAdSwoB80prlqB78Z0DoGWNVfAV7oyqt4jx9nyC3OqDpvUJzRtgcgnUeyvdL1hy85BYYtjd9X9UyE85UPSrU2m75wVDmoCHKY+lj9agaMJOwYhSsJpyjSfk5V+R3oBCf647xhC2/OFcvgd07J7T0eSnJhXldEyrB2eLfztW7OolS5zp+x7mub++Q7HD4Dq9vKfcA3c80KSA0azNrOWRuaDhwZzkza3ZicjRqZjBjMmptaGRlaDQ5aMAYADIAAQAAFRgAKgEBAAEIjmyDfTD9KjIUKCda7CFX5ufEgGGzr9rVkR/A7KUABgQAAAAAAsSKAC4AAQAAFRgBHAAyCAMAABUYXKYBdlx+6PaVeQVhZm5pYwJmcgCSuvEaqge7EIrZVpDtSjKlUNLlThD93QKTwuxSS+KCyQedCD+nHhlTWMw+vzeLrP6BsXQN4cJpEAWUE+m1zn44D4RTrBWADReb5WhzbEA0oiuVcd9g7UVSYT6zfRoFmk9kBp1bHtwrwXxKsTwK2ByYTD3qMUEMXI9uRhTlzv+7W3oXIrX2nBFDrSXrKs9SKCYzsIyZrMMbgOYjILvSeGGoRTISRbiN1HA94m/RhCTxpW4mhM40tUev8vA39T1wV/s6gfgNRRvZ5xHb6lVLozcQ1Hku7z28fQE6ZJbBQgcQJGKeGasnw1uUZD5Di4JrlAOcAy5EethoV7+kDH5D/xZzAAApBZgAAIAAAAA=","timestamp":1552922486.57306,"querytime":3,"answerfrom":"192.134.4.1"}}},"MgH4T3MlBDXU0qaxaRcnAA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"KKOEAAABAAEAAQAAA3dXVwVhRk5pYwJmUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":1,"answerfrom":"192.134.4.1","timestamp":1552922501.45319}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"HEOABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922486.95872}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"vQqEAAABAAEABQAIA25zMwNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGADHAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAAGA25zNsA8wBAAAgABAAKjAAAGA25zMcAQwBAAAgABAAKjAAAGA25zMsAQwBAAAgABAAKjAAACwAzAOAABAAEAAqMAAATBM9ANwGAAAQABAAKjAAAEwIYEAcByAAEAAQACowAABMBdAATATgABAAEAAqMAAASCOx8dwGAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcByABwAAQACowAAECABBmAwBQABAAAAAAABAALADAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwE4AHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.69848}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922486.34768,"querytime":2,"answerfrom":"192.134.4.1","data":"GviEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAQgBAQMIAwEAAdPH3dD6UIEkcVMThgn7fUgbxw8GJfXbWXrpCc7J8hR5EsNdZ7whc6oqa3ziZetJecJ318KqIT75I/yDwp3FCk6936tTdnntmDwl5CNn0F16OOtP4kZOUDs2hwWEcyz5sUPPs5v5Vuoo6/piPBXh6pS+J5AAqzLIAK11sXU67ORDVdHXIRV8VDGne3p+jV/PmSzgdAZfJ5xUxMuV76DrlWZkKIn/658Zl+Zat0y85FrsmXPYMG/8Y/l0J0jtqgaTUIRVM2ZryoKDH98UH8tmKfXK87eqKLV4RHvd/wXBgv65d1+JOrf2R3J1LQNhsyAdlED2e111VLKDgxcjc66HOPfADAAwAAEAAqMAAQgBAAMIAwEAAaqBT2ZQrM4q3xjx++/rY6vXzxam2pImOAYONiOM9xfyPH2XmBPU9ghseSe/idBMyjoLP7S017wFZymo+45PAWTSPsPUJrf2s8kOXVsuSFBr6sM2dQM3dnDRcXdGcLUrJksXrI7QGVSOFWKOcYPe1xzEbf2VP68I2sWaaMp7QMkDADew7nFxV/dP1RCMhMJ6y575TvOg7z/L1WV1d0fyxZ4miftL6z/5+u2KWDdXISUSSa/HcRSQAWtbHgWds+34lQMmXlrDWI3sq/kkWr+KDJ/M01+3cX94rGGEPAp1x4ZJiwdzLsvBTC9z8cgVpGgeAF/oxN55nL3EAvFs8AhomkPADAAwAAEAAqMAAQgBAAMIAwEAAdPrdwx/CIhOZf01Mv8PA3X3i2VzrrbC8fPXWpSMy6ki0QvyLCq6ZUf4lcPCd0VucqTztRpyrQqhYSfDBUC2tTgAbfNtL/icoWKO0QNyPlilWlsNE5mvQ0cqORRNX0V2AZTReComqVivpe81tgJFL8HMFxqRtcIA1ngo2i4atZ5STm05dBXZzlPxpFvwefHRZW3VfF3wudskg8PHCLvfOhuSie/tWEd2iBfba80i8pe96iZGJZ5YZFTf9kZeWcCC5E6pZtDstlUTyII0DZxo2OZ42YBUR4pzAcCLU6wGodZLGJcPXOb/wS1BmzNI3r+OaKr8N/0YD8xJuQEmJ/dN64fADAAuAAEAAqMAARwAMAgCAAKjAFyxUIBciZEWkuoFYWZuaWMCZnIAT0EnEMVHTIAnBuuSwkCZDNXAkpMDT0E60KXi5iaiqcWyXXOW9b/yXEBFKlpG72kp9v2qtBzpPsZHafk+7A4B6BomBEoM8SuDCqt6Tdz/apNXp3mc3sewL9jF56ALPGolozmIEFZmstBUn4NcBxntRTzxH+GyH5UB9Jn4TJVq+wGVUx6QYdg9L8uAjkcP3jBQAb8L1czmemiF2r1MMbTCPYRebdK5gQFhdgREOXwj2mc8RjcPxuud/CM01vxnQwYobCKt+deeropJi//JN13RPGJ2siexMxe8IOlmhq9Qfzoik2OwwPtfQTIVHdRcw0KbiDLAGGoNLKCmBcsDQxnbeQAAKQWYAACAAAAA"}}},"K1aB+vxTZc0RW5SYUgX9og":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"t0iEAAABAAIABAAJBWFmbmljAmZyAAAzAAHADAAzAAEAAAAAAA0BAAABCI5sg30w/SoywAwALgABAAAAAAEcADMIAgAAAABcpv4zXH7M1pV5BWFmbmljAmZyAFfsUFsQQjK7+dJx4f01f8eJiyA3QTpObw/QXSBeJwC5eqsSgpQnLt0aUx5DiTBh5XVC8/p/Sd7+9LTVWr2xbtMksfH6/B/Z5gqnYb/PRCexnD0KAUK5/maIJALx7Uso6oNGWlzEzvE23MoqK4D7nWBRM7VdxFS6QZXCPRQL/5i76MTzZQgbunlmIpHvMluX0g099U3PPBz39D6hN8zElBEsLRnggVAs9XItdvrkzbkLb4juGqCX5PrvX4dgu5zCHfS7BnPm9u365dg9H3QNXbGmy6J+QGVz16nwZve5abAJJdIvIXEHX3HeuoQZu4ax5Ctmv88iDZkIgL2eUpnmuxbADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsFrwAwAAgABAAKjAAAGA25zM8FrwAwALgABAAKjAAEcAAIIAgACowBcs7FhXIundpV5BWFmbmljAmZyACdbKfVWsliE9A2ebMQ7BDYx3kIzwe3cA29nCvrjoWYhrkG+0BXWNznY6ebbgcJaRjctvCodOUXitEgl8xrl1+4xeFecaoQVbZWvH15Eqxdbf6wDH0vrlxo+Nfo3xXg5K8mG8zHp9E1R6xLkyzfRYzXHiVCKJGuh6HDHafN2Tm+jQ7TuXubpQvCj1ErKlTlw0Qo6ipyg7xBV0OR6ZfM724HJe4BlmbA/xjgoVr44kK+c7wc6H2bU53ukAu68rdvLne4ohcHkFW6ZC29k5tgnOQ+x2w8EuR/Se/xSn2DhjkKya/SUxAsmeIXrdgxKtzlCHE6q4b0GBggTcL0TwsIKkCfBZwABAAEAAqMAAATAhgQBwX0AAQABAAKjAAAEwF0ABMGPAAEAAQACowAABMCGADHBZwAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwX0AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsGPABwAAQACowAAECABBmAwBgABAAAAAAABAAHBZwAuAAEAAqMAARoAAQgDAAKjAFyyUqdcirP/lXkDbmljAmZyACQ1MMymYxOY4F9HiZb1rs6Cn1/nGfTKGDFpsxMMlhz8kWl9dOZxRuuFuxEQ5k3BTce1eS9pXA0Ua3CMCjxGH1kA3ZI753Smo2aBC5u6Cc3HcxHtSVXgOq3PpM8YAqUlrw3ObDP3mHH45CeweV3mVK9FXbpyQzw9KVBmiwABzG7fGmvZctx8pmsQvjwPLiWhcop8nohhip8q2A4Meymn/kbkjRRLrxGgD/V8Pdy6UXmGnxgBlPuLweFl1D7c3AY1IqfhmDULqPhJMoOXiII63gLFm8Ur75YV9LprEGWKH212PfYxy03+jQ9y0TG3u5+HI7tNiOFn9Fov+9kzvseAzfbBZwAuAAEAAqMAARoAHAgDAAKjAFywo2hciQTElXkDbmljAmZyAItAZLkoaAKnpNJrhr2J9PC2W6fPhNAhFk2ppm/AVHTZKfKIdqBczWzlv6QPudz4OBlL3yf1u20tSovabDFLpr3CH6bu8tJHPBZCE+ve2HBjpAAndv/MZKoWqfL7g1Va3DMhi1YSeU/ipXRQxWmBSfAaZ13e/+VaBlMTj3JK5/2fcnqeS8AKoIMgPIe19H9itHHI5T3FWb2WDgq5gOPwqGJeqV6uO65GPIzG85zFeMFfn1uW34vqiFKUveV6sDUh9d1sWtJJwC0kR0nx9SqGNsHdK8q/Q2PaD/S3wqOd3cEs3rS/ANH04c59VKVOQfsyLR7l+OfNf9cp8aw1veoRcioAACkFmAAAgAAAAA==","querytime":2,"timestamp":1552922486.38618,"answerfrom":"192.134.4.1"}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1552922487.76312,"answerfrom":"192.134.4.1","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"0TlRUFu34T/7a+Z9BxkQVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9+6EAAABAAEAAQAAA1dXdwVBRm5pYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922501.37072}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","querytime":1,"answerfrom":"192.134.4.1","timestamp":1552922487.84341}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922481.92687,"data":"cGaEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwHUAAQABAAKjAAAEwIYEAcBjAAEAAQACowAABMBdAATAhwABAAEAAqMAAATAhgAxwHUAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBjABwAAQACowAAECABBmAwBQABAAAAAAABAALAhwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922486.68736,"data":"Q1KEAAABAAEABQAIA25zMgNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAoDbnM2A2V4dMAQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnMxwGzAjAABAAEAAqMAAATBM9ANwEQAAQABAAKjAAAEwIYEAcAMAAEAAQACowAABMBdAATAVgABAAEAAqMAAATAhgAxwGgAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAVgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwGgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922478.37999,"data":"JdeEAAABAAMAAAAGBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zM8AqwDwAAQABAAKjAAAEwIYEAcAmAAEAAQACowAABMBdAATATgABAAEAAqMAAATAhgAxwDwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAmABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.66256,"data":"Pw2EAAABAAEABQAIA25zMQNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAHAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAAGA25zMsAQwBAAAgABAAKjAAAGA25zM8AQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnM2wDzAOAABAAEAAqMAAATBM9ANwE4AAQABAAKjAAAEwF0ABMBgAAEAAQACowAABMCGADHAgAABAAEAAqMAAASCOx8dwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBOABwAAQACowAAECABBmAwBQABAAAAAAABAALAYAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwIAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Bl2ABQABAAAAAAAAAAACAAE=","timestamp":1552922487.34253,"querytime":1,"answerfrom":"192.134.4.1"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922486.67861,"data":"77GEAAABAAEABQAIA25zMgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMBdAATAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAYDbnMxwFzAgAABAAEAAqMAAATBM9ANwG4AAQABAAKjAAAEwIYEAcA4AAEAAQACowAABMCGADHAWAABAAEAAqMAAASCOx8dwG4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAOAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwFgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922487.68894}}},"KsaIpCVhpeMsuVQal+DZhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1552922487.52758,"answerfrom":"192.134.4.1","data":"38eEAAABAAEAAQAAA3dXVwVhZk5pQwJGUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"yryABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","timestamp":1552922486.96693,"querytime":1,"answerfrom":"192.134.4.1"}}}} -b.ns.se 2001:067c:254c:0301:0000:0000:0000:0053 {} -b.ns.se 192.36.133.107 {} -b.gtld-servers.net 2001:0503:231d:0000:0000:0000:0002:0030 {} -b.gtld-servers.net 192.33.14.30 {} -z.ns.se 185.159.198.150 {"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"pXCAAAABAAAAAwAEAm5zA25pYwJzZQAAAQABwA8AAgABAAFRgAAGA25zM8APwA8AAgABAAFRgAAHAWkCbnPAE8APAAIAAQABUYAAAsAMwAwAAQABAAFRgAAEW+IkLcAnAAEAAQABUYAABFviJS3ADAAcAAEAAVGAABAgAQZ8EkwQCgAAAAAAAABFwCcAHAABAAFRgAAQIAEGfBJMIAcAAAAAAAAARQ==","timestamp":1552922484.409,"querytime":32,"answerfrom":"185.159.198.150"}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"answerfrom":"185.159.198.150","timestamp":1552922484.57116,"data":"O+WAAAABAAAAAwAEAm5zA25pYwJzZQAAHAABwA8AAgABAAFRgAACwAzADwACAAEAAVGAAAcBaQJuc8ATwA8AAgABAAFRgAAGA25zM8APwAwAHAABAAFRgAAQIAEGfBJMEAoAAAAAAAAARcAMAAEAAQABUYAABFviJC3ASAABAAEAAVGAAARb4iUtwEgAHAABAAFRgAAQIAEGfBJMIAcAAAAAAAAARQ=="}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"JsiAAAABAAAAAwAEA25zMwNuaWMCc2UAAAEAAcAQAAIAAQABUYAAAsAMwBAAAgABAAFRgAAHAWkCbnPAFMAQAAIAAQABUYAABQJuc8AQwAwAAQABAAFRgAAEW+IlLcBJAAEAAQABUYAABFviJC3ADAAcAAEAAVGAABAgAQZ8EkwgBwAAAAAAAABFwEkAHAABAAFRgAAQIAEGfBJMEAoAAAAAAAAARQ==","timestamp":1552922482.2516,"querytime":32,"answerfrom":"185.159.198.150"}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"185.159.198.150","querytime":32,"timestamp":1552922482.42319,"data":"kp2AAAABAAAAAwAEA25zMwNuaWMCc2UAABwAAcAQAAIAAQABUYAABwFpAm5zwBTAEAACAAEAAVGAAAUCbnPAEMAQAAIAAQABUYAAAsAMwAwAHAABAAFRgAAQIAEGfBJMIAcAAAAAAAAARcAMAAEAAQABUYAABFviJS3AOwABAAEAAVGAAARb4iQtwDsAHAABAAFRgAAQIAEGfBJMEAoAAAAAAAAARQ=="}}}} -ns6.ext.nic.fr 130.59.31.29 {"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"M0yEAAABAAEAAAAAA25zMQNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAE=","answerfrom":"130.59.31.29","querytime":19,"timestamp":1552922478.49284}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922477.60222,"querytime":20,"answerfrom":"130.59.31.29","data":"yVmEAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwgAkAQ=="}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"3x6EAAABAAEAAAAACWRuc21hc3RlcgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAI=","answerfrom":"130.59.31.29","querytime":19,"timestamp":1552922488.22212}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"WxKEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"130.59.31.29","querytime":20,"timestamp":1552922477.4727}}},"e4zVImtII38cTuTEUukPnA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.58586,"querytime":20,"answerfrom":"130.59.31.29","data":"n6+EAAABAAEAAAAAAWQDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwAUEAg=="}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"qIiEAAABAAEAAAAAA25zMgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMBdAAQ=","timestamp":1552922478.72349,"querytime":20,"answerfrom":"130.59.31.29"}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":20,"answerfrom":"130.59.31.29","timestamp":1552922477.24112,"data":"esyEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIg=="}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":20,"answerfrom":"130.59.31.29","timestamp":1552922479.0578,"data":"WRmEAAABAAEAAAAAA25zMwNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBgABAAAAAAABAAE="}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"YPaEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwpJqLg==","answerfrom":"130.59.31.29","querytime":20,"timestamp":1552922477.35352}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","querytime":19,"timestamp":1552922478.9479,"data":"Xs2EAAABAAEAAAAAA25zMwNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGADE="}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922477.12451,"querytime":20,"answerfrom":"130.59.31.29","data":"HEeEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwbCQFg=="}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2X+EAAABAAEAAAAAAWQDbmljAmZyAAABAAHADAABAAEAAqMAAATCAAkB","querytime":19,"timestamp":1552922476.88995,"answerfrom":"130.59.31.29"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"CCCEAAABAAEAAAAAA25zMgNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAAI=","querytime":20,"timestamp":1552922478.81979,"answerfrom":"130.59.31.29"}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"+oqEAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"130.59.31.29","querytime":20,"timestamp":1552922477.72396}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":20,"timestamp":1552922477.01045,"answerfrom":"130.59.31.29","data":"E3OEAAABAAEAAAAAAWQDbmljAmZyAAAcAAHADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAAB"}}},"EFDojD6JfBcmEaLwh9qkiA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":20,"timestamp":1552922476.68883,"answerfrom":"130.59.31.29","data":"gAWEAAABAAEAAAAAAWQDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAg=="}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":20,"timestamp":1552922488.3409,"answerfrom":"130.59.31.29","data":"G2SEAAABAAAAAQAACWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAYAAQAAFRgAI8AMCmhvc3RtYXN0ZXLAFnhX+wIAABwgAAAHCAAk6gAAABUY"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"SkWEAAABAAEAAAAAA25zMQNuaWMCZnIAABwAAcAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAE=","timestamp":1552922478.60599,"querytime":20,"answerfrom":"130.59.31.29"}}}} -ns6.ext.nic.fr 2001:0620:0000:00ff:0000:0000:0000:002f {"e4zVImtII38cTuTEUukPnA":null,"SwbApgSh9mA6B3tXP/GkUg":null,"kPMzuIR4sho5B5qTORsFWg":null,"PBlOUdigoXz0ayiyPtsxqw":null,"/ad1wr3S5M12N8m7PKZMlg":null,"dqdRTp15sptDjWNc+Ny8fA":null,"RE9uT98EtnHNKxECV54OHw":null,"0Y1OnyHHHeY64pWuEem8Fg":null,"SPBaDls0FbzNIjP4Lr4aZg":null,"f39y8o9yMy+2CJ3Tild/Hw":null,"QIgmZF9uZaI1+8Y34m3zGw":null,"8QJIMKoYjfeWdcOUu1cfeg":null,"EFDojD6JfBcmEaLwh9qkiA":null,"Lm8PThfvr4TSG5Oo/xY+Jw":null,"td2cmo+X37Q32z0SfAhp8Q":null,"adP+90hFKW1Uzd20+9yl5g":null,"v3bnT3mxa7enSqJowgjjtA":null,"BoP52jf+VIVO3VrSWkuPQQ":null} -ns1.arin.net 2001:0500:0013:0000:0000:0000:0000:0108 {} -ns1.arin.net 199.212.0.108 {} -c.root-servers.net 192.33.4.12 {} -b.ip6-servers.arpa 199.253.182.182 {} -b.ip6-servers.arpa 2001:0500:0086:0000:0000:0000:0000:0086 {} -e.in-addr-servers.arpa 203.119.86.101 {} -e.in-addr-servers.arpa 2001:0dd8:0006:0000:0000:0000:0000:0101 {} -z.arin.net 199.212.0.63 {"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"199.212.0.63","querytime":78,"timestamp":1552922480.44012,"data":"46OAAAABAAAABQAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAEAACAAEAAqMAABgGcmVzb25lDHVuaXYtcmVubmVzMQJmcgDAEAACAAEAAqMAAAoDbnMxA25pY8BJwBAAAgABAAKjAAAGA25zM8BdwBAAAgABAAKjAAAGA25zMsBdwBAAAgABAAKjAAANAm5zBHJpcGUDbmV0AA=="}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"6dmAAAABAAAAAwAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAPAAIAAQACowAADANuczEDbmljAmZyAMAPAAIAAQACowAABgNuczPAO8APAAIAAQACowAABgNuczLAOw==","querytime":78,"answerfrom":"199.212.0.63","timestamp":1552922481.29495}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"vMWAAAABAAAAAwAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwA4AAgABAAKjAAAMA25zMwNuaWMCZnIAwA4AAgABAAKjAAAGA25zMsA6wA4AAgABAAKjAAAGA25zMcA6","querytime":78,"timestamp":1552922480.11476,"answerfrom":"199.212.0.63"}}}} -d.root-servers.net 2001:0500:002d:0000:0000:0000:0000:000d {} +g.root-servers.net 2001:0500:0012:0000:0000:0000:0000:0d0d {} +g.root-servers.net 192.112.36.4 {} +ns6.ext.nic.fr 2001:0620:0000:00ff:0000:0000:0000:002f {"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875437.9314,"querytime":39,"answerfrom":"2001:620:0:ff::2f","data":"NqCEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIg=="}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"GD+EAAABAAEAAAAAAWQDbmljAmZyAAABAAHADAABAAEAAqMAAATCAAkB","timestamp":1626875437.37234,"querytime":39}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"L0eEAAABAAEAAAAAA25zMwNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGADE=","timestamp":1626875439.91922,"querytime":39}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"liyEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwpJqLg==","timestamp":1626875438.09105,"querytime":39}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875439.4374,"data":"s6uEAAABAAEAAAAAA25zMQNuaWMCZnIAABwAAcAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAE=","answerfrom":"2001:620:0:ff::2f"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"ZWWEAAABAAEAAAAAA25zMgNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAAI=","timestamp":1626875439.75359,"querytime":44}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"buiEAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwgAkAQ==","timestamp":1626875438.3901,"querytime":40}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875440.06735,"querytime":39,"answerfrom":"2001:620:0:ff::2f","data":"7gGEAAABAAEAAAAAA25zMwNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBgABAAAAAAABAAE="}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"KzKEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwbCQFg==","timestamp":1626875437.77883,"querytime":39}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875456.22291,"querytime":39,"answerfrom":"2001:620:0:ff::2f","data":"A7aEAAABAAAAAQAACWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAYAAQAAFRgAI8AMCmhvc3RtYXN0ZXLAFnh3HOsAABwgAAAHCAAk6gAAABUY"}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"P6yEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","timestamp":1626875438.23121,"querytime":39}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"802EAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"2001:620:0:ff::2f","querytime":40,"timestamp":1626875438.53491}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:620:0:ff::2f","data":"s2SEAAABAAEAAAAACWRuc21hc3RlcgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAI=","timestamp":1626875456.06842,"querytime":39}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.28874,"querytime":39,"answerfrom":"2001:620:0:ff::2f","data":"rROEAAABAAEAAAAAA25zMQNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAE="}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875437.52294,"data":"uQSEAAABAAEAAAAAAWQDbmljAmZyAAAcAAHADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAAB","answerfrom":"2001:620:0:ff::2f"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.60268,"querytime":40,"answerfrom":"2001:620:0:ff::2f","data":"kt6EAAABAAEAAAAAA25zMgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMBdAAQ="}}}} +ns6.ext.nic.fr 130.59.31.29 {"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","data":"X62EAAABAAEAAAAAA25zMgNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAAI=","timestamp":1626875468.51398,"querytime":39}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","data":"SC+EAAABAAEAAAAAA25zMQNuaWMCZnIAABwAAcAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAE=","timestamp":1626875468.26373,"querytime":39}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875471.00508,"querytime":39,"answerfrom":"130.59.31.29","data":"XYCEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIg=="}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875467.76613,"data":"OlWEAAABAAEAAAAAAWQDbmljAmZyAAABAAHADAABAAEAAqMAAATCAAkB","answerfrom":"130.59.31.29"}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","data":"QH+EAAABAAEAAAAAA25zMwNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGADE=","timestamp":1626875468.67644,"querytime":39}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875471.16774,"data":"rxaEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwpJqLg==","answerfrom":"130.59.31.29"}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"QUiEAAABAAEAAAAAAWUDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwbCQFg==","answerfrom":"130.59.31.29","querytime":39,"timestamp":1626875470.8453}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"BdeEAAABAAEAAAAAA25zMwNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBgABAAAAAAABAAE=","answerfrom":"130.59.31.29","querytime":39,"timestamp":1626875468.8417}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875471.51136,"data":"Ne6EAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAAQABwAwAAQABAAKjAAAEwgAkAQ==","answerfrom":"130.59.31.29"}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875471.67632,"querytime":39,"answerfrom":"130.59.31.29","data":"PTGEAAABAAEAAAAAAWcDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"NZGEAAABAAAAAQAACWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAYAAQAAFRgAI8AMCmhvc3RtYXN0ZXLAFnh3HOsAABwgAAAHCAAk6gAAABUY","answerfrom":"130.59.31.29","querytime":39,"timestamp":1626875474.47938}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875471.33682,"data":"dYOEAAABAAEAAAAAAWYDZXh0A25pYwJmcgAAHAABwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"130.59.31.29"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"qtuEAAABAAEAAAAAA25zMgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMBdAAQ=","answerfrom":"130.59.31.29","querytime":39,"timestamp":1626875468.39205}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":39,"timestamp":1626875467.9362,"data":"PJOEAAABAAEAAAAAAWQDbmljAmZyAAAcAAHADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAAB","answerfrom":"130.59.31.29"}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","data":"FD2EAAABAAEAAAAACWRuc21hc3RlcgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAI=","timestamp":1626875474.31556,"querytime":39}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"130.59.31.29","data":"2suEAAABAAEAAAAAA25zMQNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAE=","timestamp":1626875468.13887,"querytime":39}}}} +f.ip6-servers.arpa 193.0.9.2 {"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.0.9.2","data":"qciAAAABAAAABQAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAAMA25zNAVhcG5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAADQVyaXJucwRhcmluwHE=","timestamp":1626875469.08089,"querytime":11}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.0.9.2","data":"VimAAAABAAAABQAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAPA25zNAVhcG5pYwNuZXQAwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAcMBAAAIAAQABUYAADQVyaXJucwRhcmluwHDAQAACAAEAAVGAAA4DbnMzB2FmcmluaWPAcMBAAAIAAQABUYAADQNuczMGbGFjbmljwHA=","timestamp":1626875469.88438,"querytime":11}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"NLOAAAABAAAABQAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQBXJpcm5zBGFyaW4DbmV0AMBAAAIAAQABUYAAEwNwcmkHYXV0aGRucwRyaXBlwHHAQAACAAEAAVGAAAwDbnM0BWFwbmljwHHAQAACAAEAAVGAAA4DbnMzB2FmcmluaWPAccBAAAIAAQABUYAADQNuczMGbGFjbmljwHE=","answerfrom":"193.0.9.2","querytime":10,"timestamp":1626875470.3004}}}} +f.ip6-servers.arpa 2001:067c:00e0:0000:0000:0000:0000:0002 {"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875442.38912,"querytime":10,"answerfrom":"2001:67c:e0::2","data":"qIOAAAABAAAABQAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAADANuczQFYXBuaWPAccBAAAIAAQABUYAADQVyaXJucwRhcmluwHE="}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"mEeAAAABAAAABQAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAADANuczQFYXBuaWPAccBAAAIAAQABUYAADQVyaXJucwRhcmluwHE=","answerfrom":"2001:67c:e0::2","querytime":11,"timestamp":1626875440.77354}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875443.10518,"querytime":10,"answerfrom":"2001:67c:e0::2","data":"aAKAAAABAAAABQAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAADANuczQFYXBuaWPAccBAAAIAAQABUYAADQVyaXJucwRhcmluwHE="}}}} d.root-servers.net 199.7.91.13 {} -ns.nic.se 91.226.36.45 {"B17azTRyHl7a9Vo8oZH1fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922484.71155,"querytime":54,"answerfrom":"91.226.36.45","data":"k4WEAAABAAMAAAAECnpvbmVtYXN0ZXIDbmV0AAACAAHADAACAAEAAA4QAAwDbnMyA25pYwJmcgDADAACAAEAAA4QAAsCbnMDbmljAnNlAMAMAAIAAQAADhAABgNuczPAR8BEAAEAAQAADhAABFviJC3ARAAcAAEAAA4QABAgAQZ8EkwQCgAAAAAAAABFwFsAAQABAAAOEAAEW+IlLcBbABwAAQAADhAAECABBnwSTCAHAAAAAAAAAEU="}}},"5BrASPi6wmwbUi8r1+EWyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922484.85784,"querytime":54,"answerfrom":"91.226.36.45","data":"rtSAAAABAAAAAgAECWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAAgABwAwAAgABAAAOEAAGA25zMcAMwAwAAgABAAAOEAAGA25zMsAMwDYAAQABAAAOEAAEW+IkNMA2ABwAAQAADhAAECoACAEA8AEGAAAAAAAAAFLASAABAAEAAA4QAARb4iQ1wEgAHAABAAAOEAAQKgAIAQDwAQYAAAAAAAAAUw=="}}}} -ns.nic.se 2001:067c:124c:100a:0000:0000:0000:0045 {"5BrASPi6wmwbUi8r1+EWyA":null,"B17azTRyHl7a9Vo8oZH1fg":null} +d.root-servers.net 2001:0500:002d:0000:0000:0000:0000:000d {} y.ns.se 185.159.197.150 {} +b.ns.se 192.36.133.107 {} +b.ns.se 2001:067c:254c:0301:0000:0000:0000:0053 {} +c.ip6-servers.arpa 196.216.169.11 {} +c.ip6-servers.arpa 2001:43f8:0110:0000:0000:0000:0000:0011 {} +ns1.asnlookup.zonemaster.net 45.155.96.69 {"5BrASPi6wmwbUi8r1+EWyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"45.155.96.69","data":"voyEAAABAAIAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAAgABwAwAAgABAAACWAAGA25zMsAMwAwAAgABAAACWAAGA25zMcAMwAwAAgABAAACWAACwEjADAACAAEAAAJYAALANsA2AAEAAQAAAlgABC2bYEbASAABAAEAAAJYAAQtm2BF","timestamp":1626875446.17025,"querytime":1}}},"IKQNXmg3BtKJAYq9clrK6g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875446.33501,"data":"pmGEAAABAAIAAgACATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgdvcmlnaW42CWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAEAABwAwAEAABAAA4QAA2NTI0ODUgfCAyMDAxOjY3YzoyMjE4OjovNDcgfCBGUiB8IHJpcGVuY2MgfCAyMDExLTA0LTI4wAwAEAABAAA4QAA2NTI0ODUgfCAyMDAxOjY3YzoyMjE4OjovNDggfCBGUiB8IHJpcGVuY2MgfCAyMDExLTA0LTI4wFQAAgABAAACWAAGA25zMcBUwFQAAgABAAACWAAGA25zMsBUwRQAAQABAAACWAAELZtgRsECAAEAAQAAAlgABC2bYEU=","answerfrom":"45.155.96.69"}}},"ldhoYrQfMMgR0AUdKtd9qw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"45.155.96.69","data":"KVuEAAABAAMAAgACATEBNAMxMzQDMTkyBm9yaWdpbglhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABAAAcAMABAAAQAAOEAAMjEyNDg1IHwgMTkyLjEzNC40LjAvMjMgfCBGUiB8IHJpcGVuY2MgfCAxOTkzLTA0LTIxwAwAEAABAAA4QAAyMTIyMDAgfCAxOTIuMTM0LjAuMC8xNyB8IEZSIHwgcmlwZW5jYyB8IDE5OTMtMDQtMjHADAAQAAEAADhAADIxMjQ4NSB8IDE5Mi4xMzQuNC4wLzIyIHwgRlIgfCByaXBlbmNjIHwgMTk5My0wNC0yMcAfAAIAAQAAAlgABgNuczLAH8AfAAIAAQAAAlgABgNuczHAH8EDAAEAAQAAAlgABC2bYEbBFQABAAEAAAJYAAQtm2BF","timestamp":1626875446.32475,"querytime":1}}}} +h.root-servers.net 198.97.190.53 {} +h.root-servers.net 2001:0500:0001:0000:0000:0000:0000:0053 {} +c.gtld-servers.net 192.26.92.30 {} +ns1.nic.fr 192.134.4.1 {"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":37,"timestamp":1626875448.60135,"data":"9dCEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","answerfrom":"192.134.4.1"}}},"K1aB+vxTZc0RW5SYUgX9og":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"WFCEAAABAAIABQABBWFmbmljAmZyAAAzAAHADAAzAAEAAAAAAA0BAAABCFculSbcD0TAwAwALgABAAAAAABcADMNAgAAAABhEsThYOrRQ6hmBWFmbmljAmZyALb4E23mHiEDHrugpu/qQAnyOs3XAi0Y7cGzRzMkeHLbYGtzAOusNOpPDlgw/v9s9bGrCjjlU6P6ZMITtk6CWsHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zM8CrwAwAAgABAAKjAAAGA25zMcCrwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875448.52132}}},"X5EINI94ZoFjRgtYt3T+tA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"SJWABQABAAAAAAAAAnhhAAACAAE=","timestamp":1626875475.22976,"querytime":37}}},"amIcOxD+RSRHr9RBm4BZsw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"iEqEAAABAAIAAwAABWFmbmljAmZyAAAPAAHADAAPAAEAAqMAAAwACgNteDUDbmljwBLADAAPAAEAAqMAAAgAFANteDTALMAMAAIAAQACowAABgNuczPALMAMAAIAAQACowAABgNuczLALMAMAAIAAQACowAABgNuczHALA==","timestamp":1626875455.88738,"querytime":36}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"IcGABQABAAAAAAAAAAACAAE=","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875453.23156}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875453.55403,"data":"CmiEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1"}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"IwSEAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","timestamp":1626875448.83586,"querytime":37}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875438.61377,"querytime":36,"answerfrom":"192.134.4.1","data":"XTKEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAw"}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"kc+EAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAQMNJj7sU0efcgyyjrOV9l2LSJUGdzly/DxDP9REsSOSrmdLtH2gsEr1P4bi03bX4LoFqE86cBMqbLr3kJwXs6CGP8AMADAAAQACowAARAEAAw307AA1W9UJNWXgmP/lJYmQJtQ7DjFcR+2eqIq3LeuiCf+Pa0bo+ZdhtZMmECher3saxbIV+dexmn95oNjVz5PowAwAMAABAAKjAABEAQADDXqVr9YQ6UTO57CE4nNy98avtyzDmIMku0itK/B0sPr9RW02g8HPCUulQDShqu8d/C69ZtQ19te+z6H0NPcHhmXADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA","timestamp":1626875448.1162,"querytime":37}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875451.28763,"data":"QsqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","answerfrom":"192.134.4.1"}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"7r6EAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczHARsAMAAIAAQACowAABgNuczPARg==","timestamp":1626875452.54728,"querytime":36}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"NgWEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875453.93378}}},"Rfpkr0zaZPr+IFWiqgpoCw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2h6EAAABAAAAAQAAA214NANuaWMCZnIAAAUAAcAQAAYAAQAAFRgALQlkbnNtYXN0ZXLAEApob3N0bWFzdGVywBB4dxzrAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875466.56517}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"4EmEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zMsAqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","timestamp":1626875449.22832,"querytime":37}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875451.23907,"data":"mkGABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","answerfrom":"192.134.4.1"}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":37,"timestamp":1626875440.12721,"data":"7zSEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zM8Aq","answerfrom":"192.134.4.1"}}},"e5t/JY6QxhgCIIAKZ3VBzw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.54564,"querytime":37,"answerfrom":"192.134.4.1","data":"2ACEAAABAAAAAQAAA1d3dwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":37,"timestamp":1626875453.97814,"data":"JLGEAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1"}}},"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"N06EAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","timestamp":1626875449.93552,"querytime":36}}},"lPK6iO4hY+qisxgYHMEdqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"xeyEAAABAAAAAQAAA3dXVwVhRk5pQwJmcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875473.3142,"querytime":36}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"42CABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","timestamp":1626875451.19156,"querytime":37}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875452.49711,"data":"y8iEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczLAOsAMAAIAAQACowAABgNuczPAOg==","answerfrom":"192.134.4.1"}}},"Y/IeIwIJb12zVtYwf7MnYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"KeSABQABAAAAAAAAAnhhAAAGAAE=","timestamp":1626875475.15515,"querytime":36}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875455.11208,"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.134.4.1"}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"pMiEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875449.89262}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"RzCEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875451.59234,"querytime":36}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":37,"timestamp":1626875454.84392,"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.134.4.1"}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875454.51792}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"jQ+EAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAw","timestamp":1626875443.59411,"querytime":84}}},"nzXGyx8fQSw1AeZRc/+Mhg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"BM2ABQABAAAAAAAAA3d3dwJ4YQAAAQAB","timestamp":1626875475.32285,"querytime":36}}},"3aY5XkMnVsoC/UHcIq6pjg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875473.49599,"data":"WI6EAAABAAAAAQAAA3d3dwVBRk5pQwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1"}}},"/juc4jRb/Y4P5xdjgc10cw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"WQOEAAABAAAAAQAAA214NQNuaWMCZnIAAAUAAcAQAAYAAQAAFRgALQlkbnNtYXN0ZXLAEApob3N0bWFzdGVywBB4dxzrAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875466.51547,"querytime":36}}}} +ns1.nic.fr 2001:067c:2218:0002:0000:0000:0004:0001 {"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"vO6EAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875453.87191}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":47,"timestamp":1626875448.88998,"data":"GDqEAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","answerfrom":"2001:67c:2218:2::4:1"}}},"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875450.04181,"querytime":46,"answerfrom":"2001:67c:2218:2::4:1","data":"8w+EAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA"}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"bPGABQABAAAAAAAAAAACAAE=","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875453.16982}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"T7eEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875453.49193,"querytime":46}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.12748,"querytime":47,"answerfrom":"2001:67c:2218:2::4:1","data":"gBSEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zMsAq"}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"9RiABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","timestamp":1626875451.38982,"querytime":46}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"NDiEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zM8AqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","timestamp":1626875449.34024,"querytime":47}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"aBiEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","timestamp":1626875449.28109,"querytime":47}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875443.49765,"querytime":82,"answerfrom":"2001:67c:2218:2::4:1","data":"7VOEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8Aw"}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"spCEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875451.53533,"querytime":47}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"J56EAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875453.81959}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875452.65679,"querytime":46,"answerfrom":"2001:67c:2218:2::4:1","data":"xEKEAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczLARg=="}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875454.45866,"querytime":46,"answerfrom":"2001:67c:2218:2::4:1","data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA=="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875454.78857}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.35769,"querytime":47,"answerfrom":"2001:67c:2218:2::4:1","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"+xqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875451.44798}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875448.24337,"querytime":48,"answerfrom":"2001:67c:2218:2::4:1","data":"9uCEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAQMNJj7sU0efcgyyjrOV9l2LSJUGdzly/DxDP9REsSOSrmdLtH2gsEr1P4bi03bX4LoFqE86cBMqbLr3kJwXs6CGP8AMADAAAQACowAARAEAAw16la/WEOlEzuewhOJzcvfGr7csw5iDJLtIrSvwdLD6/UVtNoPBzwlLpUA0oarvHfwuvWbUNfbXvs+h9DT3B4ZlwAwAMAABAAKjAABEAQADDfTsADVb1Qk1ZeCY/+UliZAm1DsOMVxH7Z6oirct66IJ/49rRuj5l2G1kyYQKF6vexrFshX517Gaf3mg2NXPk+jADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA"}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"CtiEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"2001:67c:2218:2::4:1","querytime":47,"timestamp":1626875449.98264}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"+PGEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zMsAw","answerfrom":"2001:67c:2218:2::4:1","querytime":46,"timestamp":1626875440.1779}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":46,"timestamp":1626875452.59665,"data":"QmKEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMwNuaWPAEsAMAAIAAQACowAABgNuczHAOsAMAAIAAQACowAABgNuczLAOg==","answerfrom":"2001:67c:2218:2::4:1"}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:2218:2::4:1","data":"VQ+ABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","timestamp":1626875451.3335,"querytime":46}}}} +u.arin.net 204.61.216.50 {"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875441.55299,"querytime":29,"answerfrom":"204.61.216.50","data":"IY2EAAABAAEABAAIAXoEYXJpbgNuZXQAAAEAAcAMAAEAAQAAqMAABMfUAD/ADgACAAEAAKjAAAYDbnMywA7ADgACAAEAAKjAAAYDbnMxwA7ADgACAAEAAKjAAAQBdcAOwA4AAgABAACowAAGA25zM8AOwFwAAQABAACowAAEzD3YMsBKAAEAAQAAqMAABMfUAGzAOAABAAEAAKjAAATHRwBswGwAAQABAACowAAExwUabMBcABwAAQAAqMAAECABBQAAFGBQAK0AAAAAAAHASgAcAAEAAKjAABAgAQUAABMAAAAAAAAAAAEIwDgAHAABAACowAAQIAEFAAAxAAAAAAAAAAABCMBsABwAAQAAqMAAECABBQAAqQAAAAAAAAAAAQg="}}},"lasV0cL3Q8ch+RpJm5zPYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"204.61.216.50","data":"ncOEAAABAAEABAAIBXJpcm5zBGFyaW4DbmV0AAAcAAHADAAcAAEAAKjAABAmIAA4IAAAAAAAAAAAAABTwBIAAgABAACowAAGA25zMsASwBIAAgABAACowAAGA25zM8ASwBIAAgABAACowAAEAXXAEsASAAIAAQAAqMAABgNuczHAEsBsAAEAAQAAqMAABMw92DLAfAABAAEAAKjAAATH1ABswEgAAQABAACowAAEx0cAbMBaAAEAAQAAqMAABMcFGmzAbAAcAAEAAKjAABAgAQUAABRgUACtAAAAAAABwHwAHAABAACowAAQIAEFAAATAAAAAAAAAAABCMBIABwAAQAAqMAAECABBQAAMQAAAAAAAAAAAQjAWgAcAAEAAKjAABAgAQUAAKkAAAAAAAAAAAEI","timestamp":1626875441.08434,"querytime":29}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":30,"timestamp":1626875441.70745,"data":"fJOEAAABAAEABAAIAXoEYXJpbgNuZXQAABwAAcAMABwAAQAAqMAAECABBQAAEwAAAAAAAAAAAGPADgACAAEAAKjAAAYDbnMzwA7ADgACAAEAAKjAAAQBdcAOwA4AAgABAACowAAGA25zMcAOwA4AAgABAACowAAGA25zMsAOwFYAAQABAACowAAEzD3YMsBmAAEAAQAAqMAABMfUAGzAeAABAAEAAKjAAATHRwBswEQAAQABAACowAAExwUabMBWABwAAQAAqMAAECABBQAAFGBQAK0AAAAAAAHAZgAcAAEAAKjAABAgAQUAABMAAAAAAAAAAAEIwHgAHAABAACowAAQIAEFAAAxAAAAAAAAAAABCMBEABwAAQAAqMAAECABBQAAqQAAAAAAAAAAAQg=","answerfrom":"204.61.216.50"}}},"UBMcusig6w3synYwbp7omA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":29,"timestamp":1626875440.93488,"data":"L1mEAAABAAEABAAIBXJpcm5zBGFyaW4DbmV0AAABAAHADAABAAEAAKjAAATH/fk1wBIAAgABAACowAAGA25zM8ASwBIAAgABAACowAAGA25zMcASwBIAAgABAACowAAEAXXAEsASAAIAAQAAqMAABgNuczLAEsBgAAEAAQAAqMAABMw92DLATgABAAEAAKjAAATH1ABswHAAAQABAACowAAEx0cAbMA8AAEAAQAAqMAABMcFGmzAYAAcAAEAAKjAABAgAQUAABRgUACtAAAAAAABwE4AHAABAACowAAQIAEFAAATAAAAAAAAAAABCMBwABwAAQAAqMAAECABBQAAMQAAAAAAAAAAAQjAPAAcAAEAAKjAABAgAQUAAKkAAAAAAAAAAAEI","answerfrom":"204.61.216.50"}}}} +u.arin.net 2001:0500:0014:6050:00ad:0000:0000:0001 {} +dnsmaster.nic.fr 192.134.4.2 {"4YeoSYuQtm/OKn1ycfuP2Q":null} +c.ns.se 2001:067c:2554:0301:0000:0000:0000:0053 {} +c.ns.se 192.36.135.107 {} +b.in-addr-servers.arpa 2001:0500:0087:0000:0000:0000:0000:0087 {} +b.in-addr-servers.arpa 199.253.183.183 {} +ns3.nic.se 91.226.37.45 {"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875445.65007,"data":"QJ2EAAABAAEAAgADAm5zA25pYwJzZQAAAQABwAwAAQABAAAOEAAEW+IkLcAPAAIAAQAADhAABgNuczPAD8APAAIAAQAADhAAAsAMwDcAAQABAAAOEAAEW+IlLcAMABwAAQAADhAAECABBnwSTBAKAAAAAAAAAEXANwAcAAEAAA4QABAgAQZ8EkwgBwAAAAAAAABF","answerfrom":"91.226.37.45"}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875445.72704,"data":"jQOEAAABAAEAAgADAm5zA25pYwJzZQAAHAABwAwAHAABAAAOEAAQIAEGfBJMEAoAAAAAAAAARcAPAAIAAQAADhAABgNuczPAD8APAAIAAQAADhAAAsAMwAwAAQABAAAOEAAEW+IkLcBDAAEAAQAADhAABFviJS3AQwAcAAEAAA4QABAgAQZ8EkwgBwAAAAAAAABF","answerfrom":"91.226.37.45"}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"etOEAAABAAEAAgADA25zMwNuaWMCc2UAABwAAcAMABwAAQAADhAAECABBnwSTCAHAAAAAAAAAEXAEAACAAEAAA4QAAUCbnPAEMAQAAIAAQAADhAAAsAMwEQAAQABAAAOEAAEW+IkLcAMAAEAAQAADhAABFviJS3ARAAcAAEAAA4QABAgAQZ8EkwQCgAAAAAAAABF","answerfrom":"91.226.37.45","querytime":1,"timestamp":1626875444.35794}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875444.28103,"data":"ltKEAAABAAEAAgADA25zMwNuaWMCc2UAAAEAAcAMAAEAAQAADhAABFviJS3AEAACAAEAAA4QAALADMAQAAIAAQAADhAABQJuc8AQwEYAAQABAAAOEAAEW+IkLcBGABwAAQAADhAAECABBnwSTBAKAAAAAAAAAEXADAAcAAEAAA4QABAgAQZ8EkwgBwAAAAAAAABF","answerfrom":"91.226.37.45"}}}} +ns3.nic.se 2001:067c:124c:2007:0000:0000:0000:0045 {"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"tTSEAAABAAEAAwAECnpvbmVtYXN0ZXIDbmV0AAAGAAHADAAGAAEAAA4QADACbnMDbmljAnNlAApob3N0bWFzdGVyA2lpc8AzYPbx1QAAKjAAAA4QAAk6gAAADhDADAACAAEAAA4QAAwDbnMyA25pYwJmcgDADAACAAEAAA4QAAYDbnMzwC/ADAACAAEAAA4QAALALMAsABwAAQAADhAAECABBnwSTBAKAAAAAAAAAEXAgAAcAAEAAA4QABAgAQZ8EkwgBwAAAAAAAABFwCwAAQABAAAOEAAEW+IkLcCAAAEAAQAADhAABFviJS0=","answerfrom":"2001:67c:124c:2007::45","querytime":1,"timestamp":1626875444.57182}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"R5yAAAABAAAAAgACA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAQAAIAAQAADhAAAsAMwBAAAgABAAAOEAAGA25zMsAQwAwAAQABAAACWAAELZtgRcBIAAEAAQAAAlgABC2bYEY=","answerfrom":"2001:67c:124c:2007::45","querytime":1,"timestamp":1626875446.15003}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875444.3694,"data":"nMGAAAABAAAAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwAwAAgABAAAOEAAGA25zMcAMwAwAAgABAAAOEAAGA25zMsAMwDYAAQABAAACWAAELZtgRcBIAAEAAQAAAlgABC2bYEY=","answerfrom":"2001:67c:124c:2007::45"}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1626875445.97394,"data":"wPyAAAABAAAAAgACA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAQAAIAAQAADhAAAsAMwBAAAgABAAAOEAAGA25zMsAQwAwAAQABAAACWAAELZtgRcBIAAEAAQAAAlgABC2bYEY=","answerfrom":"2001:67c:124c:2007::45"}}}} +f.root-servers.net 2001:0500:002f:0000:0000:0000:0000:000f {} +f.root-servers.net 192.5.5.241 {} +ns1.ext.nic.fr 193.51.208.13 {} +a.ip6-servers.arpa 2620:0037:e000:0000:0000:0000:0000:0053 {} +a.ip6-servers.arpa 199.180.182.53 {} +a.in-addr-servers.arpa 2620:0037:e000:0000:0000:0000:0000:0053 {} +a.in-addr-servers.arpa 199.180.182.53 {} +x.ns.se 213.108.25.4 {} +i.gtld-servers.net 192.43.172.30 {} +e.ext.nic.fr 2a00:0d78:0000:0102:0193:0176:0144:0022 {"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.09931,"querytime":26,"answerfrom":"2a00:d78:0:102:193:176:144:22","data":"MPGAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3ADAABAAEAAqMAAATAhgQBwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBMAAEAAQACowAABMBdAATATAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.60552,"querytime":26,"answerfrom":"2a00:d78:0:102:193:176:144:22","data":"wbCAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3APgABAAEAAqMAAATAhgQBwD4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2a00:d78:0:102:193:176:144:22","data":"rf2AAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3ADAABAAEAAqMAAATAhgQBwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBMAAEAAQACowAABMBdAATATAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1626875446.84322,"querytime":26}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":26,"timestamp":1626875447.35597,"data":"jOyAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3APgABAAEAAqMAAATAhgQBwD4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","answerfrom":"2a00:d78:0:102:193:176:144:22"}}}} +e.ext.nic.fr 193.176.144.22 {"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.176.144.22","data":"tGKAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3APgABAAEAAqMAAATAhgQBwD4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1626875447.56704,"querytime":30}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.176.144.22","data":"6aOAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3ADAABAAEAAqMAAATAhgQBwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBMAAEAAQACowAABMBdAATATAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1626875447.06267,"querytime":29}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"nl6AAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3APgABAAEAAqMAAATAhgQBwD4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","answerfrom":"193.176.144.22","querytime":29,"timestamp":1626875447.31893}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.176.144.22","data":"LCKAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczbALMAoAAEAAQACowAABMEz0A3ADAABAAEAAqMAAATAhgQBwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBMAAEAAQACowAABMBdAATATAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwF4AAQABAAKjAAAEwIYAMcBeABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcAABAAEAAqMAAASCOx8dwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1626875446.80123,"querytime":29}}}} av1.nstld.com 192.42.177.30 {} av1.nstld.com 2001:0500:0124:0000:0000:0000:0000:0030 {} -e.ip6-servers.arpa 2001:0dd8:0006:0000:0000:0000:0000:0101 {} -e.ip6-servers.arpa 203.119.86.101 {} -b.in-addr-servers.arpa 199.253.183.183 {} -b.in-addr-servers.arpa 2001:0500:0087:0000:0000:0000:0000:0087 {} +f.in-addr-servers.arpa 193.0.9.1 {"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":11,"timestamp":1626875469.37911,"data":"DNGAAAABAAAABgAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBQAAgABAAFRgAAMAXIEYXJpbgNuZXQAwBQAAgABAAFRgAAEAXXAOMAUAAIAAQABUYAABAF4wDjAFAACAAEAAVGAAAQBecA4wBQAAgABAAFRgAAEAXrAOMAUAAIAAQABUYAAFARhcmluB2F1dGhkbnMEcmlwZcA9","answerfrom":"193.0.9.1"}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875469.6799,"querytime":10,"answerfrom":"193.0.9.1","data":"d8mAAAABAAAABgAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAEwACAAEAAVGAAAwBcgRhcmluA25ldADAEwACAAEAAVGAAAQBdcA3wBMAAgABAAFRgAAEAXjAN8ATAAIAAQABUYAABAF5wDfAEwACAAEAAVGAAAQBesA3wBMAAgABAAFRgAAUBGFyaW4HYXV0aGRucwRyaXBlwDw="}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.0.9.1","data":"2o+AAAABAAAABgAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAVAAIAAQABUYAADAFyBGFyaW4DbmV0AMAVAAIAAQABUYAABAF1wDnAFQACAAEAAVGAAAQBeMA5wBUAAgABAAFRgAAEAXnAOcAVAAIAAQABUYAABAF6wDnAFQACAAEAAVGAABQEYXJpbgdhdXRoZG5zBHJpcGXAPg==","timestamp":1626875470.09483,"querytime":10}}}} +f.in-addr-servers.arpa 2001:067c:00e0:0000:0000:0000:0000:0001 {"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":11,"timestamp":1626875441.41723,"data":"4zaAAAABAAAABgAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBQAAgABAAFRgAAMAXIEYXJpbgNuZXQAwBQAAgABAAFRgAAEAXXAOMAUAAIAAQABUYAABAF4wDjAFAACAAEAAVGAAAQBecA4wBQAAgABAAFRgAAEAXrAOMAUAAIAAQABUYAAFARhcmluB2F1dGhkbnMEcmlwZcA9","answerfrom":"2001:67c:e0::1"}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:e0::1","data":"kM6AAAABAAAABgAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAEwACAAEAAVGAAAwBeQRhcmluA25ldADAEwACAAEAAVGAABQEYXJpbgdhdXRoZG5zBHJpcGXAPMATAAIAAQABUYAABAF1wDfAEwACAAEAAVGAAAQBcsA3wBMAAgABAAFRgAAEAXjAN8ATAAIAAQABUYAABAF6wDc=","timestamp":1626875442.03887,"querytime":10}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":11,"timestamp":1626875442.74093,"data":"dgyAAAABAAAABgAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAVAAIAAQABUYAADAFyBGFyaW4DbmV0AMAVAAIAAQABUYAABAF1wDnAFQACAAEAAVGAAAQBeMA5wBUAAgABAAFRgAAEAXnAOcAVAAIAAQABUYAABAF6wDnAFQACAAEAAVGAABQEYXJpbgdhdXRoZG5zBHJpcGXAPg==","answerfrom":"2001:67c:e0::1"}}}} +ns2.nic.fr 192.93.0.4 {"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","data":"srCEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczPAOsAMAAIAAQACowAABgNuczHAOg==","timestamp":1626875452.71966,"querytime":41}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875451.02966,"querytime":42,"answerfrom":"192.93.0.4","data":"OvuABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ=="}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","data":"6f+EAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zMsAw","timestamp":1626875440.25007,"querytime":41}}},"lPK6iO4hY+qisxgYHMEdqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.36677,"querytime":41,"answerfrom":"192.93.0.4","data":"UVSEAAABAAAAAQAAA3dXVwVhRk5pQwJmcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"5+OABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875451.13862}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AMOEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAAMN9OwANVvVCTVl4Jj/5SWJkCbUOw4xXEftnqiKty3rogn/j2tG6PmXYbWTJhAoXq97GsWyFfnXsZp/eaDY1c+T6MAMADAAAQACowAARAEAAw16la/WEOlEzuewhOJzcvfGr7csw5iDJLtIrSvwdLD6/UVtNoPBzwlLpUA0oarvHfwuvWbUNfbXvs+h9DT3B4ZlwAwAMAABAAKjAABEAQEDDSY+7FNHn3IMso6zlfZdi0iVBnc5cvw8Qz/URLEjkq5nS7R9oLBK9T+G4tN21+C6BahPOnATKmy695CcF7Oghj/ADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875448.31098}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875450.10157,"querytime":42,"answerfrom":"192.93.0.4","data":"STuEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875455.20471,"querytime":42}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875454.66883,"querytime":42,"answerfrom":"192.93.0.4","data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA=="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875455.0441}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AESEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875451.64206}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","data":"4PGEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875454.03224,"querytime":42}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875452.77099,"querytime":42,"answerfrom":"192.93.0.4","data":"4auEAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMwNuaWPAEsAMAAIAAQACowAABgNuczLARsAMAAIAAQACowAABgNuczHARg=="}}},"3aY5XkMnVsoC/UHcIq6pjg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.60537,"querytime":42,"answerfrom":"192.93.0.4","data":"6W+EAAABAAAAAQAAA3d3dwVBRk5pQwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875443.6876,"querytime":84,"answerfrom":"192.93.0.4","data":"ScmEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMcAw"}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","data":"8KeEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMxwDDADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMywDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","timestamp":1626875449.41187,"querytime":41}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875451.08592,"querytime":42,"answerfrom":"192.93.0.4","data":"mSOABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ=="}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"gqmEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zMcAqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875449.46146}}},"e5t/JY6QxhgCIIAKZ3VBzw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"26WEAAABAAAAAQAAA1d3dwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875473.6584}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"+AWEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zM8Aq","answerfrom":"192.93.0.4","querytime":41,"timestamp":1626875440.30143}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":41,"timestamp":1626875453.60723,"data":"/uqEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.93.0.4"}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875453.27509,"querytime":41,"answerfrom":"192.93.0.4","data":"yn2ABQABAAAAAAAAAAACAAE="}}},"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875450.15424,"data":"dumEAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"192.93.0.4"}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"0zCEAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.93.0.4","querytime":42,"timestamp":1626875454.08606}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"C0mEAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","answerfrom":"192.93.0.4","querytime":44,"timestamp":1626875448.95839}}}} +ns2.nic.fr 192.134.4.1 {"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9dCEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875448.60135}}},"K1aB+vxTZc0RW5SYUgX9og":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"WFCEAAABAAIABQABBWFmbmljAmZyAAAzAAHADAAzAAEAAAAAAA0BAAABCFculSbcD0TAwAwALgABAAAAAABcADMNAgAAAABhEsThYOrRQ6hmBWFmbmljAmZyALb4E23mHiEDHrugpu/qQAnyOs3XAi0Y7cGzRzMkeHLbYGtzAOusNOpPDlgw/v9s9bGrCjjlU6P6ZMITtk6CWsHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zM8CrwAwAAgABAAKjAAAGA25zMcCrwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875448.52132}}},"X5EINI94ZoFjRgtYt3T+tA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875475.22976,"querytime":37,"answerfrom":"192.134.4.1","data":"SJWABQABAAAAAAAAAnhhAAACAAE="}}},"amIcOxD+RSRHr9RBm4BZsw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"iEqEAAABAAIAAwAABWFmbmljAmZyAAAPAAHADAAPAAEAAqMAAAwACgNteDUDbmljwBLADAAPAAEAAqMAAAgAFANteDTALMAMAAIAAQACowAABgNuczPALMAMAAIAAQACowAABgNuczLALMAMAAIAAQACowAABgNuczHALA==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875455.88738}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875453.23156,"querytime":36,"answerfrom":"192.134.4.1","data":"IcGABQABAAAAAAAAAAACAAE="}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875453.55403,"data":"CmiEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1"}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875448.83586,"querytime":37,"answerfrom":"192.134.4.1","data":"IwSEAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA"}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875438.61377,"data":"XTKEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAw","answerfrom":"192.134.4.1"}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"kc+EAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAQMNJj7sU0efcgyyjrOV9l2LSJUGdzly/DxDP9REsSOSrmdLtH2gsEr1P4bi03bX4LoFqE86cBMqbLr3kJwXs6CGP8AMADAAAQACowAARAEAAw307AA1W9UJNWXgmP/lJYmQJtQ7DjFcR+2eqIq3LeuiCf+Pa0bo+ZdhtZMmECher3saxbIV+dexmn95oNjVz5PowAwAMAABAAKjAABEAQADDXqVr9YQ6UTO57CE4nNy98avtyzDmIMku0itK/B0sPr9RW02g8HPCUulQDShqu8d/C69ZtQ19te+z6H0NPcHhmXADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA","timestamp":1626875448.1162,"querytime":37}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875451.28763,"querytime":35,"answerfrom":"192.134.4.1","data":"QsqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB"}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7r6EAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczHARsAMAAIAAQACowAABgNuczPARg==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875452.54728}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875453.93378,"data":"NgWEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1"}}},"Rfpkr0zaZPr+IFWiqgpoCw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"2h6EAAABAAAAAQAAA214NANuaWMCZnIAAAUAAcAQAAYAAQAAFRgALQlkbnNtYXN0ZXLAEApob3N0bWFzdGVywBB4dxzrAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875466.56517,"querytime":36}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":37,"timestamp":1626875449.22832,"data":"4EmEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zMsAqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"192.134.4.1"}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875451.23907,"querytime":36,"answerfrom":"192.134.4.1","data":"mkGABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ=="}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7zSEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zM8Aq","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875440.12721}}},"e5t/JY6QxhgCIIAKZ3VBzw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2ACEAAABAAAAAQAAA1d3dwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875473.54564}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875453.97814,"querytime":37,"answerfrom":"192.134.4.1","data":"JLGEAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875449.93552,"querytime":36,"answerfrom":"192.134.4.1","data":"N06EAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA"}}},"lPK6iO4hY+qisxgYHMEdqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"xeyEAAABAAAAAQAAA3dXVwVhRk5pQwJmcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875473.3142,"querytime":36}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"42CABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"192.134.4.1","querytime":37,"timestamp":1626875451.19156}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"y8iEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczLAOsAMAAIAAQACowAABgNuczPAOg==","timestamp":1626875452.49711,"querytime":36}}},"Y/IeIwIJb12zVtYwf7MnYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875475.15515,"data":"KeSABQABAAAAAAAAAnhhAAAGAAE=","answerfrom":"192.134.4.1"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.11208,"querytime":36,"answerfrom":"192.134.4.1","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875449.89262,"querytime":36,"answerfrom":"192.134.4.1","data":"pMiEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA"}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875451.59234,"querytime":36,"answerfrom":"192.134.4.1","data":"RzCEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875454.84392,"querytime":37,"answerfrom":"192.134.4.1","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","answerfrom":"192.134.4.1","querytime":36,"timestamp":1626875454.51792}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875443.59411,"querytime":84,"answerfrom":"192.134.4.1","data":"jQ+EAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAw"}}},"nzXGyx8fQSw1AeZRc/+Mhg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","data":"BM2ABQABAAAAAAAAA3d3dwJ4YQAAAQAB","timestamp":1626875475.32285,"querytime":36}}},"3aY5XkMnVsoC/UHcIq6pjg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.49599,"querytime":36,"answerfrom":"192.134.4.1","data":"WI6EAAABAAAAAQAAA3d3dwVBRk5pQwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"/juc4jRb/Y4P5xdjgc10cw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875466.51547,"querytime":36,"answerfrom":"192.134.4.1","data":"WQOEAAABAAAAAQAAA214NQNuaWMCZnIAAAUAAcAQAAYAAQAAFRgALQlkbnNtYXN0ZXLAEApob3N0bWFzdGVywBB4dxzrAAAcIAAABwgAJOoAAAAVGA=="}}}} +ns2.nic.fr 2001:0660:3005:0001:0000:0000:0001:0002 {"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875454.94777,"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"2001:660:3005:1::1:2"}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","answerfrom":"2001:660:3005:1::1:2","querytime":35,"timestamp":1626875454.7227}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875452.86849,"data":"tHCEAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczHARg==","answerfrom":"2001:660:3005:1::1:2"}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3005:1::1:2","data":"3CKEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875451.6948,"querytime":35}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3005:1::1:2","data":"0+yEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875454.13897,"querytime":35}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"z4SEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8Aw","answerfrom":"2001:660:3005:1::1:2","querytime":70,"timestamp":1626875443.78079}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"nruABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"2001:660:3005:1::1:2","querytime":35,"timestamp":1626875450.88689}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"V2mEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczPAOsAMAAIAAQACowAABgNuczHAOg==","answerfrom":"2001:660:3005:1::1:2","querytime":35,"timestamp":1626875452.81896}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3005:1::1:2","data":"YGqEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAw","timestamp":1626875440.36106,"querytime":35}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875448.36692,"querytime":35,"answerfrom":"2001:660:3005:1::1:2","data":"vi6EAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAAMNepWv1hDpRM7nsITic3L3xq+3LMOYgyS7SK0r8HSw+v1FbTaDwc8JS6VANKGq7x38Lr1m1DX2177PofQ09weGZcAMADAAAQACowAARAEBAw0mPuxTR59yDLKOs5X2XYtIlQZ3OXL8PEM/1ESxI5KuZ0u0faCwSvU/huLTdtfgugWoTzpwEypsuveQnBezoIY/wAwAMAABAAKjAABEAQADDfTsADVb1Qk1ZeCY/+UliZAm1DsOMVxH7Z6oirct66IJ/49rRuj5l2G1kyYQKF6vexrFshX517Gaf3mg2NXPk+jADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA"}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.20995,"data":"VmSEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"2001:660:3005:1::1:2"}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3005:1::1:2","data":"+lKABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","timestamp":1626875450.98037,"querytime":36}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.26187,"querytime":35,"answerfrom":"2001:660:3005:1::1:2","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875453.65844,"data":"oraEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"2001:660:3005:1::1:2"}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9K+EAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zM8Aq","answerfrom":"2001:660:3005:1::1:2","querytime":35,"timestamp":1626875440.40495}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875453.32512,"data":"5WGABQABAAAAAAAAAAACAAE=","answerfrom":"2001:660:3005:1::1:2"}}},"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.25392,"data":"vJmEAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"2001:660:3005:1::1:2"}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"JP2EAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","answerfrom":"2001:660:3005:1::1:2","querytime":36,"timestamp":1626875449.01635}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875454.18549,"data":"m8OEAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"2001:660:3005:1::1:2"}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2AqEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMxwDDADAACAAEAAqMAAAYDbnMywDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","answerfrom":"2001:660:3005:1::1:2","querytime":35,"timestamp":1626875449.52158}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875449.56877,"data":"n8OEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zM8AqwAwAAgABAAKjAAAGA25zMsAqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"2001:660:3005:1::1:2"}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.93577,"data":"9FyABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","answerfrom":"2001:660:3005:1::1:2"}}}} +k.gtld-servers.net 192.52.178.30 {} +l.gtld-servers.net 192.41.162.30 {} +av4.nstld.com 192.82.134.30 {"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"KOiEAAABAAEABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwAwAAQABAAFRgAAEwAUGHsAOAAIAAQABUYAADwNhdjEFbnN0bGQDY29tAMAOAAIAAQABUYAABgNhdjPARMAOAAIAAQABUYAABgNhdjLARMAOAAIAAQABUYAABgNhdjTARA==","answerfrom":"192.82.134.30","querytime":2,"timestamp":1626875445.13857}}},"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.34216,"querytime":12,"answerfrom":"192.82.134.30","data":"Wj2EAAABAAEABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwAwAHAABAAFRgAAQIAEFA6g+AAAAAAAAAAIAMMAOAAIAAQABUYAADwNhdjEFbnN0bGQDY29tAMAOAAIAAQABUYAABgNhdjPAUMAOAAIAAQABUYAABgNhdjLAUMAOAAIAAQABUYAABgNhdjTAUA=="}}}} +av4.nstld.com 2001:0500:0127:0000:0000:0000:0000:0030 {"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"timestamp":1626875444.99278,"data":"+T2EAAABAAEABAAHA2F2NAVuc3RsZANjb20AAAEAAcAMAAEAAQAAASwABMBShh7AEAACAAEAAVGAAALADMAQAAIAAQABUYAABgNhdjLAEMAQAAIAAQABUYAABgNhdjPAEMAQAAIAAQABUYAABgNhdjHAEMAMABwAAQAAASwAECABBQABJwAAAAAAAAAAADDASQABAAEAAAEsAATAKrIewEkAHAABAAABLAAQIAEFAAElAAAAAAAAAAAAMMBbAAEAAQAAASwABMBShR7AWwAcAAEAAAEsABAgAQUAASYAAAAAAAAAAAAwwG0AAQABAAABLAAEwCqxHsBtABwAAQAAASwAECABBQABJAAAAAAAAAAAADA=","answerfrom":"2001:500:127::30"}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.12409,"querytime":2,"answerfrom":"2001:500:127::30","data":"V32EAAABAAEABAAHA2F2NAVuc3RsZANjb20AABwAAcAMABwAAQAAASwAECABBQABJwAAAAAAAAAAADDAEAACAAEAAVGAAALADMAQAAIAAQABUYAABgNhdjLAEMAQAAIAAQABUYAABgNhdjPAEMAQAAIAAQABUYAABgNhdjHAEMAMAAEAAQAAASwABMBShh7AVQABAAEAAAEsAATAKrIewFUAHAABAAABLAAQIAEFAAElAAAAAAAAAAAAMMBnAAEAAQAAASwABMBShR7AZwAcAAEAAAEsABAgAQUAASYAAAAAAAAAAAAwwHkAAQABAAABLAAEwCqxHsB5ABwAAQAAASwAECABBQABJAAAAAAAAAAAADA="}}}} +b.gtld-servers.net 192.33.14.30 {} +b.gtld-servers.net 2001:0503:231d:0000:0000:0000:0002:0030 {} +z.ns.se 185.159.198.150 {"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"YrCAAAABAAAAAgAEAm5zA25pYwJzZQAAAQABwA8AAgABAAFRgAAGA25zM8APwA8AAgABAAFRgAACwAzADAABAAEAAVGAAARb4iQtwCcAAQABAAFRgAAEW+IlLcAMABwAAQABUYAAECABBnwSTBAKAAAAAAAAAEXAJwAcAAEAAVGAABAgAQZ8EkwgBwAAAAAAAABF","answerfrom":"185.159.198.150","querytime":4,"timestamp":1626875445.63441}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875445.71463,"data":"Hh+AAAABAAAAAgAEAm5zA25pYwJzZQAAHAABwA8AAgABAAFRgAACwAzADwACAAEAAVGAAAYDbnMzwA/ADAAcAAEAAVGAABAgAQZ8EkwQCgAAAAAAAABFwAwAAQABAAFRgAAEW+IkLcA1AAEAAQABUYAABFviJS3ANQAcAAEAAVGAABAgAQZ8EkwgBwAAAAAAAABF","answerfrom":"185.159.198.150"}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1626875444.34495,"data":"ZwmAAAABAAAAAgAEA25zMwNuaWMCc2UAABwAAcAQAAIAAQABUYAABQJuc8AQwBAAAgABAAFRgAACwAzADAAcAAEAAVGAABAgAQZ8EkwgBwAAAAAAAABFwAwAAQABAAFRgAAEW+IlLcAoAAEAAQABUYAABFviJC3AKAAcAAEAAVGAABAgAQZ8EkwQCgAAAAAAAABF","answerfrom":"185.159.198.150"}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1626875444.26057,"data":"27iAAAABAAAAAgAEA25zMwNuaWMCc2UAAAEAAcAQAAIAAQABUYAAAsAMwBAAAgABAAFRgAAFAm5zwBDADAABAAEAAVGAAARb4iUtwDYAAQABAAFRgAAEW+IkLcAMABwAAQABUYAAECABBnwSTCAHAAAAAAAAAEXANgAcAAEAAVGAABAgAQZ8EkwQCgAAAAAAAABF","answerfrom":"185.159.198.150"}}}} +d.nic.fr 2001:0678:000c:0000:0000:0000:0000:0001 {"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875447.51559,"data":"B6CAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zNgNleHTAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczHAOsAMABwAAQACowAAECABBmAwBQABAAAAAAABAALANgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwEwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBeABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAXQAEwDYAAQABAAKjAAAEgjsfHcBMAAEAAQACowAABMCGADHAXgABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:678:c::1"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.00809,"querytime":42,"answerfrom":"2001:678:c::1","data":"YYSAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczHALMAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAKAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwEwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBeABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAhgQBwCgAAQABAAKjAAAEgjsfHcBMAAEAAQACowAABMCGADHAXgABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEwTPQDQ=="}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.26826,"querytime":42,"answerfrom":"2001:678:c::1","data":"bxmAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnM2wCzAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwD4AHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BwABwAAQACowAAECABBmAwBgABAAAAAAABAAHAUAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwD4AAQABAAKjAAAEgjsfHcBwAAEAAQACowAABMCGADHAUAABAAEAAqMAAATAhgQBwCgAAQABAAKjAAAEwTPQDQ=="}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875446.74286,"querytime":42,"answerfrom":"2001:678:c::1","data":"B/SAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczHALMAMAAEAAQACowAABMCGBAHADAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwCgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8A+ABwAAQACowAAECABBmAwBgABAAAAAAABAAHAXgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwCgAAQABAAKjAAAEgjsfHcA+AAEAAQACowAABMCGADHAXgABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEwTPQDQ=="}}}} +d.nic.fr 194.0.9.1 {"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"FW2AAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAPsAQAAIAAQACowAABgNuczPAEMAMABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAXQAEwDoAAQABAAKjAAAEgjsfHcBwAAEAAQACowAABMCGADHAKAABAAEAAqMAAATAhgQBwF4AAQABAAKjAAAEwTPQDcA6ABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AcAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwCgAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.9.1","querytime":42,"timestamp":1626875447.45892}}},"anMXpIkCCaKzuyWsrPPtLw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875437.57683,"data":"elqEAAABAAQAAAAAAmZyAAACAAHADAACAAEAAqMAAAwBZgNleHQDbmljwAzADAACAAEAAqMAAAQBZcAiwAwAAgABAAKjAAAEAWTAJsAMAAIAAQACowAABAFnwCI=","answerfrom":"194.0.9.1"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"T8OAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczbATMAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEgjsfHcAoAAEAAQACowAABMCGADHAXgABAAEAAqMAAATAXQAEwEgAAQABAAKjAAAEwTPQDcBwABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AKAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwF4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg==","answerfrom":"194.0.9.1","querytime":42,"timestamp":1626875446.95388}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875447.21803,"data":"pUOAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHALMAMAAEAAQACowAABMBdAATAKAABAAEAAqMAAASCOx8dwD4AAQABAAKjAAAEwIYAMcBQAAEAAQACowAABMCGBAHAcAABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsAoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/APgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwFAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.9.1"}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"w5WAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbAOsAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczLAEMAMAAEAAQACowAABMCGBAHATAABAAEAAqMAAASCOx8dwF4AAQABAAKjAAAEwIYAMcBwAAEAAQACowAABMBdAATANgABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBMABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AXgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwHAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg==","answerfrom":"194.0.9.1","querytime":42,"timestamp":1626875446.68854}}}} +h.gtld-servers.net 192.54.112.30 {} +i.ns.se 194.146.106.22 {} +z.arin.net 199.212.0.63 {"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"199.212.0.63","data":"8raAAAABAAAAAwAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwA4AAgABAAKjAAAMA25zMgNuaWMCZnIAwA4AAgABAAKjAAAGA25zMcA6wA4AAgABAAKjAAAGA25zM8A6","timestamp":1626875441.75449,"querytime":107}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"199.212.0.63","data":"XsuAAAABAAAAAwAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHADgACAAEAAqMAAAwDbnMzA25pYwJmcgDADgACAAEAAqMAAAYDbnMxwDnADgACAAEAAqMAAAYDbnMywDk=","timestamp":1626875442.11261,"querytime":104}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":112,"timestamp":1626875442.80985,"data":"fdmAAAABAAAAAwAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAPAAIAAQACowAADANuczMDbmljAmZyAMAPAAIAAQACowAABgNuczHAO8APAAIAAQACowAABgNuczLAOw==","answerfrom":"199.212.0.63"}}}} +a.gtld-servers.net 192.5.6.30 {"B17azTRyHl7a9Vo8oZH1fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.5649,"querytime":3,"answerfrom":"192.5.6.30","data":"HP+AAAABAAAAAwAACnpvbmVtYXN0ZXIDbmV0AAACAAHADAACAAEAAqMAAAwDbnMyA25pYwJmcgDADAACAAEAAqMAAAsCbnMDbmljAnNlAMAMAAIAAQACowAABgNuczPARw=="}}},"Ox0c7Ezn+YV8Irn2871Liw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.5.6.30","data":"3buEAAABAA0AAAAMA25ldAAAAgABwAwAAgABAAKjAAARAWQMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBbcAjwAwAAgABAAKjAAAEAWPAI8AMAAIAAQACowAABAFswCPADAACAAEAAqMAAAQBYsAjwAwAAgABAAKjAAAEAWvAI8AMAAIAAQACowAABAFhwCPADAACAAEAAqMAAAQBacAjwAwAAgABAAKjAAAEAWbAI8AMAAIAAQACowAABAFnwCPADAACAAEAAqMAAAQBasAjwAwAAgABAAKjAAAEAWjAI8AMAAIAAQACowAABAFlwCPAIQABAAEAAqMAAATAH1AewCEAHAABAAKjAAAQIAEFAIVuAAAAAAAAAAAAMMA+AAEAAQACowAABMA3Ux7APgAcAAEAAqMAABAgAQUBsfkAAAAAAAAAAAAwwE4AAQABAAKjAAAEwBpcHsBOABwAAQACowAAECABBQOD6wAAAAAAAAAAADDAXgABAAEAAqMAAATAKaIewF4AHAABAAKjAAAQIAEFANk3AAAAAAAAAAAAMMBuAAEAAQACowAABMAhDh7AbgAcAAEAAqMAABAgAQUDIx0AAAAAAAAAAgAwwH4AAQABAAKjAAAEwDSyHsB+ABwAAQACowAAECABBQMNLQAAAAAAAAAAADA=","timestamp":1626875445.36595,"querytime":3}}}} +a.gtld-servers.net 2001:0503:a83e:0000:0000:0000:0002:0030 {} j.gtld-servers.net 192.48.79.30 {} -j.ns.se 199.254.63.1 {} -g.ns.se 2001:06b0:000e:0003:0000:0000:0000:0001 {} -g.ns.se 130.239.5.114 {} -g.gtld-servers.net 192.42.93.30 {} -soleil.uvsq.fr 193.51.24.1 {"QOkoGas2H66m/7tFl3RUhA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922480.60421,"querytime":3,"answerfrom":"193.51.24.1","data":"aHiEAAABAAEAAAAABnJlc29uZQx1bml2LXJlbm5lczECZnIAAAEAAcAMAAEAAQAADhAABIEU/gE="}}},"uLlSKdcUBsdWu2uNVqHtoA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"vN+EAAABAAAAAQAABnJlc29uZQx1bml2LXJlbm5lczECZnIAABwAAcATAAYAAQAAAAAAI8AMCmhvc3RtYXN0ZXLAE3gAmCIAABwgAAAHCAAk6gAAAAAA","answerfrom":"193.51.24.1","querytime":4,"timestamp":1552922480.67487}}}} -a.root-servers.net 198.41.0.4 {"Ox0c7Ezn+YV8Irn2871Liw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"udSAAAABAAAADQAMA25ldAAAAgABwAwAAgABAAKjAAARAWUMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBZsAjwAwAAgABAAKjAAAEAW3AI8AMAAIAAQACowAABAFpwCPADAACAAEAAqMAAAQBasAjwAwAAgABAAKjAAAEAWLAI8AMAAIAAQACowAABAFhwCPADAACAAEAAqMAAAQBY8AjwAwAAgABAAKjAAAEAWvAI8AMAAIAAQACowAABAFowCPADAACAAEAAqMAAAQBbMAjwAwAAgABAAKjAAAEAWfAI8AMAAIAAQACowAABAFkwCPAIQABAAEAAqMAAATADF4ewCEAHAABAAKjAAAQIAEFAhyhAAAAAAAAAAAAMMA+AAEAAQACowAABMAjMx7APgAcAAEAAqMAABAgAQUD1BQAAAAAAAAAAAAwwE4AAQABAAKjAAAEwDdTHsBOABwAAQACowAAECABBQGx+QAAAAAAAAAAADDAXgABAAEAAqMAAATAK6wewF4AHAABAAKjAAAQIAEFAznBAAAAAAAAAAAAMMBuAAEAAQACowAABMAwTx7AbgAcAAEAAqMAABAgAQUCcJQAAAAAAAAAAAAwwH4AAQABAAKjAAAEwCEOHsB+ABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","querytime":15,"answerfrom":"198.41.0.4","timestamp":1552922483.12013}}},"anMXpIkCCaKzuyWsrPPtLw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"bryAAAABAAAABQAKAmZyAAACAAHADAACAAEAAqMAAAwBZANleHQDbmljwAzADAACAAEAAqMAAAQBZMAmwAwAAgABAAKjAAAEAWXAIsAMAAIAAQACowAABAFmwCLADAACAAEAAqMAAAQBZ8AiwCAAAQABAAKjAAAEwAUEAsA4AAEAAQACowAABMIACQHASAABAAEAAqMAAATBsJAWwFgAAQABAAKjAAAEwpJqLsBoAAEAAQACowAABMIAJAHAIAAcAAEAAqMAABAgAQUAAC4AAAAAAAAAAAACwDgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBIABwAAQACowAAECoADXgAAAECAZMBdgFEACLAWAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwGgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","querytime":15,"answerfrom":"198.41.0.4","timestamp":1552922476.46829}}}} -a.root-servers.net 2001:0503:ba3e:0000:0000:0000:0002:0030 {} -h.root-servers.net 2001:0500:0001:0000:0000:0000:803f:0235 {} -h.root-servers.net 128.63.2.53 {} -f.ns.se 2a01:03f0:0000:0305:0000:0000:0000:0053 {} -f.ns.se 192.71.53.53 {} -f.gtld-servers.net 192.35.51.30 {} -dnsmaster.nic.fr 192.134.4.2 {"4YeoSYuQtm/OKn1ycfuP2Q":null} -ns2.asnlookup.zonemaster.net 91.226.36.53 {"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":52,"timestamp":1552922485.20643,"answerfrom":"91.226.36.53","data":"fmmEAAABAAEAAgACA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAMAAEAAQABUYAABFviJDTAEAACAAEAAVGAAALADMAQAAIAAQABUYAABgNuczLAEMBYAAEAAQABUYAABFviJDXADAABAAEAAVGAAARb4iQ0"}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":53,"timestamp":1552922482.64472,"answerfrom":"91.226.36.53","data":"qjaEAAABAAEAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwAwABgABAAFRgAAiA25zMcAMBWFkbWluwAxcj49tAAAHCAAAA4QAAVGAAAADhMAMAAIAAQABUYAAAsA2wAwAAgABAAFRgAAGA25zMsAMwHIAAQABAAFRgAAEW+IkNcA2AAEAAQABUYAABFviJDQ="}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":52,"timestamp":1552922485.55028,"answerfrom":"91.226.36.53","data":"reCEAAABAAAAAQAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAQAAYAAQAAA4QAHsAMBWFkbWluwBBcj49tAAAHCAAAA4QAAVGAAAADhA=="}}}} -ns2.asnlookup.zonemaster.net 2a00:0801:00f0:0106:0000:0000:0000:0053 {} -m.root-servers.net 2001:0dc3:0000:0000:0000:0000:0000:0035 {"qa2ldaRH3Yi2ZSb8/9xaSw":null,"SwbApgSh9mA6B3tXP/GkUg":null,"gNvppsjyN6Isa7C+Jawn6w":null,"nLqvsXvTOBR/25TgODnu7g":null,"sNttT2q5QOS0nfEEkjvGLQ":null,"MRObrmyvWJVMundftQXlfg":null,"v3bnT3mxa7enSqJowgjjtA":null,"adP+90hFKW1Uzd20+9yl5g":null,"Hx/wBzZ7dxT0btOz2sXTow":null,"e4zVImtII38cTuTEUukPnA":null,"BXVBY8r9sPsznJk1A1ELew":null,"dVKTTf6INn/t4Xg/r5toXA":null,"/ad1wr3S5M12N8m7PKZMlg":null,"4YeoSYuQtm/OKn1ycfuP2Q":null,"SPBaDls0FbzNIjP4Lr4aZg":null,"txj88I7p+6ryfLcyf3NH/g":null,"KoOHMqlriAzfyyS3tJUVhQ":null,"Lm8PThfvr4TSG5Oo/xY+Jw":null,"cSyobwmK5IBS1MITWTINfw":null,"kPMzuIR4sho5B5qTORsFWg":null,"WI04J6v9tb8oD+4bFtDPog":null,"dqdRTp15sptDjWNc+Ny8fA":null,"NwmO2exDjUCQobiXIEO9Vg":null,"0Y1OnyHHHeY64pWuEem8Fg":null,"Y1ZvIYRyV6Gaf0yziEjgFw":null,"f39y8o9yMy+2CJ3Tild/Hw":null,"8QJIMKoYjfeWdcOUu1cfeg":null,"OMEA0Jt12vdHI/DIOjRKdA":null,"zQS3R7z6GBeKGH+IUB62IQ":null,"QOkoGas2H66m/7tFl3RUhA":null,"xA39FCnoMsmugxaD74lRSw":null,"uLlSKdcUBsdWu2uNVqHtoA":null,"PBlOUdigoXz0ayiyPtsxqw":null,"JHIAnHDDut/MIJng8E/fKg":null,"VZzWsSoApJvJHihc4YAsIQ":null,"29HegVtbyPWaDm48Y6S6RQ":null,"RE9uT98EtnHNKxECV54OHw":null,"DopGLtTlD8qdrSAvQzI7OA":null,"QIgmZF9uZaI1+8Y34m3zGw":null,"fMgGdfwEQk2v5buwJaqqRA":null,"EFDojD6JfBcmEaLwh9qkiA":null,"7mmtEYchsO9PZT5B8N6/Pg":null,"td2cmo+X37Q32z0SfAhp8Q":null,"BoP52jf+VIVO3VrSWkuPQQ":null,"fxPbDm0VpYQV73LLw/20YA":null} -m.root-servers.net 202.12.27.33 {"sNttT2q5QOS0nfEEkjvGLQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"mV2AAAABAAAADQAPA25ldAAABgABwAwAAgABAAKjAAARAWsMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBYsAjwAwAAgABAAKjAAAEAW3AI8AMAAIAAQACowAABAFpwCPADAACAAEAAqMAAAQBZMAjwAwAAgABAAKjAAAEAWbAI8AMAAIAAQACowAABAFjwCPADAACAAEAAqMAAAQBZ8AjwAwAAgABAAKjAAAEAWzAI8AMAAIAAQACowAABAFlwCPADAACAAEAAqMAAAQBYcAjwAwAAgABAAKjAAAEAWrAI8AMAAIAAQACowAABAFowCPAzgABAAEAAqMAAATABQYewD4AAQABAAKjAAAEwCEOHsCOAAEAAQACowAABMAaXB7AbgABAAEAAqMAAATAH1AewL4AAQABAAKjAAAEwAxeHsB+AAEAAQACowAABMAjMx7AngABAAEAAqMAAATAKl0ewO4AAQABAAKjAAAEwDZwHsBeAAEAAQACowAABMArrB7A3gABAAEAAqMAAATAME8ewCEAAQABAAKjAAAEwDSyHsCuAAEAAQACowAABMApoh7ATgABAAEAAqMAAATAN1MewM4AHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMMA+ABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","answerfrom":"202.12.27.33","querytime":4,"timestamp":1552922483.00199}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"fs2AAAABAAAABQAKA25zMwNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFlA2V4dMAQwBQAAgABAAKjAAAEAWTAKsAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZMAQwBQAAgABAAKjAAAEAWbAKsA8AAEAAQACowAABMAFBALAXAABAAEAAqMAAATCAAkBwCgAAQABAAKjAAAEwbCQFsBsAAEAAQACowAABMKSai7ATAABAAEAAqMAAATCACQBwDwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","querytime":3,"answerfrom":"202.12.27.33","timestamp":1552922478.87976}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.83844,"querytime":3,"answerfrom":"202.12.27.33","data":"DVOAAAABAAAABQAKAWQDbmljAmZyAAABAAHAEgACAAEAAqMAAAgBZwNleHTADsASAAIAAQACowAABAFkwCjAEgACAAEAAqMAAAQBZcAowBIAAgABAAKjAAACwAzAEgACAAEAAqMAAAQBZsAowAwAAQABAAKjAAAEwgAJAcA6AAEAAQACowAABMAFBALASgABAAEAAqMAAATBsJAWwGgAAQABAAKjAAAEwpJqLsAmAAEAAQACowAABMIAJAHADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAABwDoAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBKABwAAQACowAAECoADXgAAAECAZMBdgFEACLAaAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCYAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"answerfrom":"202.12.27.33","timestamp":1552922484.37829,"data":"0zGAAAABAAAACgAPAm5zA25pYwJzZQAAAQABwBMAAgABAAKjAAAHAWICbnPAE8ATAAIAAQACowAABAF4wCnAEwACAAEAAqMAAAQBacApwBMAAgABAAKjAAAEAXnAKcATAAIAAQACowAABAFmwCnAEwACAAEAAqMAAAQBesApwBMAAgABAAKjAAAEAWfAKcATAAIAAQACowAABAFhwCnAEwACAAEAAqMAAAQBasApwBMAAgABAAKjAAAEAWPAKcCaAAEAAQACowAABMAkkGvAJwABAAEAAqMAAATAJIVrwLoAAQABAAKjAAAEwCSHa8BqAAEAAQACowAABMBHNTXAigABAAEAAqMAAASC7wVywEoAAQABAAKjAAAEwpJqFsCqAAEAAQACowAABMf+PwHAOgABAAEAAqMAAATVbBkEwFoAAQABAAKjAAAEuZ/FlsB6AAEAAQACowAABLmfxpbAmgAcAAEAAqMAABAqAQPwAAADAQAAAAAAAABTwCcAHAABAAKjAAAQIAEGfCVMAwEAAAAAAAAAU8C6ABwAAQACowAAECABBnwlVAMBAAAAAAAAAFPAagAcAAEAAqMAABAqAQPwAAADBQAAAAAAAABTwIoAHAABAAKjAAAQIAEGsAAOAAMAAAAAAAAAAQ=="}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"iBeAAAABAAAABgAMATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWQLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFlwGjATAACAAEAAqMAAAQBYsBowEwAAgABAAKjAAAEAWPAaMBMAAIAAQACowAABAFhwGjATAACAAEAAqMAAAQBZsBowLIAAQABAAKjAAAEx7S2NcCSAAEAAQACowAABMf9trbAogABAAEAAqMAAATE2KkLwGYAAQABAAKjAAAEyAdWNcCCAAEAAQACowAABMt3VmXAwgABAAEAAqMAAATBAAkCwLIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CSABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAogAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwGYAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CCABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAwgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","timestamp":1552922480.83806,"querytime":4,"answerfrom":"202.12.27.33"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922478.66705,"answerfrom":"202.12.27.33","data":"rsqAAAABAAAABQAKA25zMgNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFlA2V4dMAQwBQAAgABAAKjAAAEAWfAKsAUAAIAAQACowAABAFmwCrAFAACAAEAAqMAAAQBZMAQwBQAAgABAAKjAAAEAWTAKsBsAAEAAQACowAABMAFBALAXAABAAEAAqMAAATCAAkBwCgAAQABAAKjAAAEwbCQFsBMAAEAAQACowAABMKSai7APAABAAEAAqMAAATCACQBwGwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8A8ABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"timestamp":1552922484.95199,"answerfrom":"202.12.27.33","data":"QeOAAAABAAAADQAOA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAlAAIAAQACowAAEQFlDGd0bGQtc2VydmVyc8AlwCUAAgABAAKjAAAEAWzAPMAlAAIAAQACowAABAFjwDzAJQACAAEAAqMAAAQBZMA8wCUAAgABAAKjAAAEAWbAPMAlAAIAAQACowAABAFiwDzAJQACAAEAAqMAAAQBZ8A8wCUAAgABAAKjAAAEAWrAPMAlAAIAAQACowAABAFpwDzAJQACAAEAAqMAAAQBa8A8wCUAAgABAAKjAAAEAW3APMAlAAIAAQACowAABAFhwDzAJQACAAEAAqMAAAQBaMA8wPcAAQABAAKjAAAEwAUGHsCXAAEAAQACowAABMAhDh7AZwABAAEAAqMAAATAGlwewHcAAQABAAKjAAAEwB9QHsA6AAEAAQACowAABMAMXh7AhwABAAEAAqMAAATAIzMewKcAAQABAAKjAAAEwCpdHsEHAAEAAQACowAABMA2cB7AxwABAAEAAqMAAATAK6wewLcAAQABAAKjAAAEwDBPHsDXAAEAAQACowAABMA0sh7AVwABAAEAAqMAAATAKaIewOcAAQABAAKjAAAEwDdTHsD3ABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"SnGAAAABAAAADQAOCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwCEAAgABAAKjAAARAWcMZ3RsZC1zZXJ2ZXJzwCHAIQACAAEAAqMAAAQBY8A4wCEAAgABAAKjAAAEAWnAOMAhAAIAAQACowAABAFkwDjAIQACAAEAAqMAAAQBZsA4wCEAAgABAAKjAAAEAWzAOMAhAAIAAQACowAABAFtwDjAIQACAAEAAqMAAAQBa8A4wCEAAgABAAKjAAAEAWjAOMAhAAIAAQACowAABAFhwDjAIQACAAEAAqMAAAQBZcA4wCEAAgABAAKjAAAEAWLAOMAhAAIAAQACowAABAFqwDjA0wABAAEAAqMAAATABQYewPMAAQABAAKjAAAEwCEOHsBTAAEAAQACowAABMAaXB7AcwABAAEAAqMAAATAH1AewOMAAQABAAKjAAAEwAxeHsCDAAEAAQACowAABMAjMx7ANgABAAEAAqMAAATAKl0ewMMAAQABAAKjAAAEwDZwHsBjAAEAAQACowAABMArrB7BAwABAAEAAqMAAATAME8ewLMAAQABAAKjAAAEwDSyHsCTAAEAAQACowAABMApoh7AowABAAEAAqMAAATAN1MewNMAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMA==","querytime":4,"timestamp":1552922482.08068,"answerfrom":"202.12.27.33"}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2YiAAAABAAAABgAMATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAFwACAAEAAqMAABQBYQ9pbi1hZGRyLXNlcnZlcnPAH8AXAAIAAQACowAABAFkwDfAFwACAAEAAqMAAAQBZsA3wBcAAgABAAKjAAAEAWPAN8AXAAIAAQACowAABAFiwDfAFwACAAEAAqMAAAQBZcA3wDUAAQABAAKjAAAEx7S2NcCFAAEAAQACowAABMf9t7fAdQABAAEAAqMAAATE2KkKwFUAAQABAAKjAAAEyAo8NcCVAAEAAQACowAABMt3VmXAZQABAAEAAqMAAATBAAkBwDUAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CFABwAAQACowAAECABBQAAhwAAAAAAAAAAAIfAdQAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAAQwFUAHAABAAKjAAAQIAETx3AQAAAAAAAAAAAAU8CVABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAZQAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAB","querytime":3,"timestamp":1552922480.32534,"answerfrom":"202.12.27.33"}}},"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"ZIuAAAABAAAADQAOA2F2NAVuc3RsZANjb20AAAEAAcAWAAIAAQACowAAFAFjDGd0bGQtc2VydmVycwNuZXQAwBYAAgABAAKjAAAEAWnALcAWAAIAAQACowAABAFrwC3AFgACAAEAAqMAAAQBZcAtwBYAAgABAAKjAAAEAWzALcAWAAIAAQACowAABAFiwC3AFgACAAEAAqMAAAQBbcAtwBYAAgABAAKjAAAEAWHALcAWAAIAAQACowAABAFqwC3AFgACAAEAAqMAAAQBaMAtwBYAAgABAAKjAAAEAWTALcAWAAIAAQACowAABAFnwC3AFgACAAEAAqMAAAQBZsAtwKsAAQABAAKjAAAEwAUGHsCLAAEAAQACowAABMAhDh7AKwABAAEAAqMAAATAGlwewNsAAQABAAKjAAAEwB9QHsBrAAEAAQACowAABMAMXh7A+wABAAEAAqMAAATAIzMewOsAAQABAAKjAAAEwCpdHsDLAAEAAQACowAABMA2cB7ASwABAAEAAqMAAATAK6wewLsAAQABAAKjAAAEwDBPHsBbAAEAAQACowAABMA0sh7AewABAAEAAqMAAATAKaIewJsAAQABAAKjAAAEwDdTHsCrABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1552922483.31525,"querytime":4,"answerfrom":"202.12.27.33"}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"kNWAAAABAAAABQAKCWRuc21hc3RlcgNuaWMCZnIAABwAAcAaAAIAAQACowAACAFlA2V4dMAWwBoAAgABAAKjAAAEAWbAMMAaAAIAAQACowAABAFnwDDAGgACAAEAAqMAAAQBZMAwwBoAAgABAAKjAAAEAWTAFsBiAAEAAQACowAABMAFBALAcgABAAEAAqMAAATCAAkBwC4AAQABAAKjAAAEwbCQFsBCAAEAAQACowAABMKSai7AUgABAAEAAqMAAATCACQBwGIAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsByABwAAQACowAAECABBngADAAAAAAAAAAAAAHALgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BSABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","querytime":3,"timestamp":1552922488.27286,"answerfrom":"202.12.27.33"}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"dQCAAAABAAAADQAOAXoEYXJpbgNuZXQAABwAAcATAAIAAQACowAAEQFsDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWLAKsATAAIAAQACowAABAFtwCrAEwACAAEAAqMAAAQBZ8AqwBMAAgABAAKjAAAEAWPAKsATAAIAAQACowAABAFmwCrAEwACAAEAAqMAAAQBZcAqwBMAAgABAAKjAAAEAWvAKsATAAIAAQACowAABAFowCrAEwACAAEAAqMAAAQBYcAqwBMAAgABAAKjAAAEAWnAKsATAAIAAQACowAABAFkwCrAEwACAAEAAqMAAAQBasAqwMUAAQABAAKjAAAEwAUGHsBFAAEAAQACowAABMAhDh7AdQABAAEAAqMAAATAGlwewOUAAQABAAKjAAAEwB9QHsCVAAEAAQACowAABMAMXh7AhQABAAEAAqMAAATAIzMewGUAAQABAAKjAAAEwCpdHsC1AAEAAQACowAABMA2cB7A1QABAAEAAqMAAATAK6wewPUAAQABAAKjAAAEwDBPHsClAAEAAQACowAABMA0sh7AKAABAAEAAqMAAATAKaIewFUAAQABAAKjAAAEwDdTHsDFABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1552922479.96198,"querytime":4,"answerfrom":"202.12.27.33"}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"answerfrom":"202.12.27.33","timestamp":1552922479.12971,"data":"W8mAAAABAAAABgAMATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWELaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFlwGjATAACAAEAAqMAAAQBYsBowEwAAgABAAKjAAAEAWbAaMBMAAIAAQACowAABAFkwGjATAACAAEAAqMAAAQBY8BowGYAAQABAAKjAAAEx7S2NcCSAAEAAQACowAABMf9trbAwgABAAEAAqMAAATE2KkLwLIAAQABAAKjAAAEyAdWNcCCAAEAAQACowAABMt3VmXAogABAAEAAqMAAATBAAkCwGYAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CSABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAwgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwLIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CCABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAogAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922479.00013,"answerfrom":"202.12.27.33","data":"M9eAAAABAAAABQAKA25zMwNuaWMCZnIAABwAAcAUAAIAAQACowAACAFkA2V4dMAQwBQAAgABAAKjAAAEAWTAEMAUAAIAAQACowAABAFlwCrAFAACAAEAAqMAAAQBZsAqwBQAAgABAAKjAAAEAWfAKsAoAAEAAQACowAABMAFBALAPAABAAEAAqMAAATCAAkBwEwAAQABAAKjAAAEwbCQFsBcAAEAAQACowAABMKSai7AbAABAAEAAqMAAATCACQBwCgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsA8ABwAAQACowAAECABBngADAAAAAAAAAAAAAHATAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwFwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BsABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922483.5046,"querytime":4,"answerfrom":"202.12.27.33","data":"Gp6AAAABAAAADQAOA2F2NAVuc3RsZANjb20AABwAAcAWAAIAAQACowAAFAFqDGd0bGQtc2VydmVycwNuZXQAwBYAAgABAAKjAAAEAWjALcAWAAIAAQACowAABAFiwC3AFgACAAEAAqMAAAQBacAtwBYAAgABAAKjAAAEAWXALcAWAAIAAQACowAABAFhwC3AFgACAAEAAqMAAAQBbcAtwBYAAgABAAKjAAAEAWfALcAWAAIAAQACowAABAFswC3AFgACAAEAAqMAAAQBa8AtwBYAAgABAAKjAAAEAWbALcAWAAIAAQACowAABAFjwC3AFgACAAEAAqMAAAQBZMAtwIsAAQABAAKjAAAEwAUGHsBbAAEAAQACowAABMAhDh7A6wABAAEAAqMAAATAGlwewPsAAQABAAKjAAAEwB9QHsB7AAEAAQACowAABMAMXh7A2wABAAEAAqMAAATAIzMewKsAAQABAAKjAAAEwCpdHsBLAAEAAQACowAABMA2cB7AawABAAEAAqMAAATAK6wewCsAAQABAAKjAAAEwDBPHsDLAAEAAQACowAABMA0sh7AuwABAAEAAqMAAATAKaIewJsAAQABAAKjAAAEwDdTHsCLABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"e4zVImtII38cTuTEUukPnA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922476.52289,"answerfrom":"202.12.27.33","data":"kHCAAAABAAAABQAKAWQDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAABAFmwA7AFgACAAEAAqMAAALADMAWAAIAAQACowAABAFnwA7AFgACAAEAAqMAAAQBZMASwAwAAQABAAKjAAAEwAUEAsBoAAEAAQACowAABMIACQHAKgABAAEAAqMAAATBsJAWwDoAAQABAAKjAAAEwpJqLsBYAAEAAQACowAABMIAJAHADAAcAAEAAqMAABAgAQUAAC4AAAAAAAAAAAACwGgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcAqABwAAQACowAAECoADXgAAAECAZMBdgFEACLAOgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwFgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"yQuAAAABAAAACgAPAm5zA25pYwJzZQAAHAABwBMAAgABAAKjAAAHAWYCbnPAE8ATAAIAAQACowAABAF6wCnAEwACAAEAAqMAAAQBY8ApwBMAAgABAAKjAAAEAWHAKcATAAIAAQACowAABAFpwCnAEwACAAEAAqMAAAQBeMApwBMAAgABAAKjAAAEAXnAKcATAAIAAQACowAABAFiwCnAEwACAAEAAqMAAAQBasApwBMAAgABAAKjAAAEAWfAKcBaAAEAAQACowAABMAkkGvAmgABAAEAAqMAAATAJIVrwEoAAQABAAKjAAAEwCSHa8AnAAEAAQACowAABMBHNTXAugABAAEAAqMAAASC7wVywGoAAQABAAKjAAAEwpJqFsCqAAEAAQACowAABMf+PwHAegABAAEAAqMAAATVbBkEwIoAAQABAAKjAAAEuZ/FlsA6AAEAAQACowAABLmfxpbAWgAcAAEAAqMAABAqAQPwAAADAQAAAAAAAABTwJoAHAABAAKjAAAQIAEGfCVMAwEAAAAAAAAAU8BKABwAAQACowAAECABBnwlVAMBAAAAAAAAAFPAJwAcAAEAAqMAABAqAQPwAAADBQAAAAAAAABTwLoAHAABAAKjAAAQIAEGsAAOAAMAAAAAAAAAAQ==","timestamp":1552922484.55296,"querytime":4,"answerfrom":"202.12.27.33"}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"knCAAAABAAAABQAKAWYDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZ8AOwBYAAgABAAKjAAAEAWTADsAWAAIAAQACowAABAFkwBLAFgACAAEAAqMAAAQBZcAOwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8AMAAEAAQACowAABMKSai7ASAABAAEAAqMAAATABQQCwFgAAQABAAKjAAAEwgAJAcBoAAEAAQACowAABMGwkBbAOAABAAEAAqMAAATCACQBwEgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHAaAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwDgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","timestamp":1552922477.40512,"querytime":4,"answerfrom":"202.12.27.33"}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"answerfrom":"202.12.27.33","timestamp":1552922476.2963,"data":"4MSAAAABAAAABQAKBWFmbmljAmZyAAAGAAHAEgACAAEAAqMAAAwBZgNleHQDbmljwBLAEgACAAEAAqMAAAQBZMAswBIAAgABAAKjAAAEAWTAKMASAAIAAQACowAABAFnwCjAEgACAAEAAqMAAAQBZcAowE4AAQABAAKjAAAEwAUEAsA+AAEAAQACowAABMIACQHAbgABAAEAAqMAAATBsJAWwCYAAQABAAKjAAAEwpJqLsBeAAEAAQACowAABMIAJAHATgAcAAEAAqMAABAgAQUAAC4AAAAAAAAAAAACwD4AHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBuABwAAQACowAAECoADXgAAAECAZMBdgFEACLAJgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwF4AHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"OquAAAABAAAADQAPAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwBsAAgABAAKjAAAEAWzADsAbAAIAAQACowAABAFkwA7AGwACAAEAAqMAAAQBbcAOwBsAAgABAAKjAAAEAWPADsAbAAIAAQACowAABAFlwA7AGwACAAEAAqMAAAQBYsAOwBsAAgABAAKjAAACwAzAGwACAAEAAqMAAAQBaMAOwBsAAgABAAKjAAAEAWbADsAbAAIAAQACowAABAFpwA7AGwACAAEAAqMAAAQBa8AOwBsAAgABAAKjAAAEAWfADsAbAAIAAQACowAABAFqwA7ADAAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAwwAwAAQABAAKjAAAEwAUGHsCAAAEAAQACowAABMAhDh7AYAABAAEAAqMAAATAGlwewEAAAQABAAKjAAAEwB9QHsBwAAEAAQACowAABMAMXh7ArgABAAEAAqMAAATAIzMewN4AAQABAAKjAAAEwCpdHsCeAAEAAQACowAABMA2cB7AvgABAAEAAqMAAATAK6wewO4AAQABAAKjAAAEwDBPHsDOAAEAAQACowAABMA0sh7AMAABAAEAAqMAAATAKaIewFAAAQABAAKjAAAEwDdTHsCAABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","answerfrom":"202.12.27.33","querytime":4,"timestamp":1552922483.73236}}},"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"answerfrom":"202.12.27.33","timestamp":1552922482.73703,"data":"XNKAAAABAAAADQAOCnpvbmVtYXN0ZXIDbmV0AAAGAAHAFwACAAEAAqMAABEBZAxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFqwC7AFwACAAEAAqMAAAQBaMAuwBcAAgABAAKjAAAEAWPALsAXAAIAAQACowAABAFswC7AFwACAAEAAqMAAAQBa8AuwBcAAgABAAKjAAAEAWnALsAXAAIAAQACowAABAFmwC7AFwACAAEAAqMAAAQBbcAuwBcAAgABAAKjAAAEAWfALsAXAAIAAQACowAABAFlwC7AFwACAAEAAqMAAAQBYsAuwBcAAgABAAKjAAAEAWHALsD5AAEAAQACowAABMAFBh7A6QABAAEAAqMAAATAIQ4ewGkAAQABAAKjAAAEwBpcHsAsAAEAAQACowAABMAfUB7A2QABAAEAAqMAAATADF4ewKkAAQABAAKjAAAEwCMzHsDJAAEAAQACowAABMAqXR7AWQABAAEAAqMAAATANnAewJkAAQABAAKjAAAEwCusHsBJAAEAAQACowAABMAwTx7AiQABAAEAAqMAAATANLIewHkAAQABAAKjAAAEwCmiHsC5AAEAAQACowAABMA3Ux7A+QAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw"}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"answerfrom":"202.12.27.33","timestamp":1552922482.40536,"data":"iYCAAAABAAAACgAPA25zMwNuaWMCc2UAABwAAcAUAAIAAQACowAABwF5Am5zwBTAFAACAAEAAqMAAAQBeMAqwBQAAgABAAKjAAAEAWnAKsAUAAIAAQACowAABAF6wCrAFAACAAEAAqMAAAQBYcAqwBQAAgABAAKjAAAEAWPAKsAUAAIAAQACowAABAFiwCrAFAACAAEAAqMAAAQBZsAqwBQAAgABAAKjAAAEAWrAKsAUAAIAAQACowAABAFnwCrAawABAAEAAqMAAATAJJBrwIsAAQABAAKjAAAEwCSFa8B7AAEAAQACowAABMAkh2vAmwABAAEAAqMAAATARzU1wLsAAQABAAKjAAAEgu8FcsBLAAEAAQACowAABMKSahbAqwABAAEAAqMAAATH/j8BwDsAAQABAAKjAAAE1WwZBMAoAAEAAQACowAABLmfxZbAWwABAAEAAqMAAAS5n8aWwGsAHAABAAKjAAAQKgED8AAAAwEAAAAAAAAAU8CLABwAAQACowAAECABBnwlTAMBAAAAAAAAAFPAewAcAAEAAqMAABAgAQZ8JVQDAQAAAAAAAABTwJsAHAABAAKjAAAQKgED8AAAAwUAAAAAAAAAU8C7ABwAAQACowAAECABBrAADgADAAAAAAAAAAE="}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922478.7749,"querytime":3,"answerfrom":"202.12.27.33","data":"KbGAAAABAAAABQAKA25zMgNuaWMCZnIAABwAAcAUAAIAAQACowAACAFmA2V4dMAQwBQAAgABAAKjAAAEAWTAEMAUAAIAAQACowAABAFlwCrAFAACAAEAAqMAAAQBZ8AqwBQAAgABAAKjAAAEAWTAKsBsAAEAAQACowAABMAFBALAPAABAAEAAqMAAATCAAkBwEwAAQABAAKjAAAEwbCQFsAoAAEAAQACowAABMKSai7AXAABAAEAAqMAAATCACQBwGwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsA8ABwAAQACowAAECABBngADAAAAAAAAAAAAAHATAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BcABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","querytime":3,"timestamp":1552922477.65451,"data":"jVWAAAABAAAABQAKAWcDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAABAFkwA7AFgACAAEAAqMAAAQBZsAOwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZMASwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAcAMAAEAAQACowAABMIAJAHAOgABAAEAAqMAAATABQQCwGgAAQABAAKjAAAEwgAJAcAqAAEAAQACowAABMGwkBbASgABAAEAAqMAAATCkmouwDoAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEoAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw=="}}},"QOkoGas2H66m/7tFl3RUhA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"oMuAAAABAAAABQAKBnJlc29uZQx1bml2LXJlbm5lczECZnIAAAEAAcAgAAIAAQACowAADAFkA2V4dANuaWPAIMAgAAIAAQACowAABAFkwDrAIAACAAEAAqMAAAQBZsA2wCAAAgABAAKjAAAEAWfANsAgAAIAAQACowAABAFlwDbANAABAAEAAqMAAATABQQCwEwAAQABAAKjAAAEwgAJAcB8AAEAAQACowAABMGwkBbAXAABAAEAAqMAAATCkmouwGwAAQABAAKjAAAEwgAkAcA0ABwAAQACowAAECABBQAALgAAAAAAAAAAAALATAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAABwHwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsBcABwAAQACowAAECABBnwQEAARAAAAAAAAAFPAbAAcAAEAAqMAABAgAQZ4AEwAAAAAAAAAAAAB","timestamp":1552922480.55253,"querytime":3,"answerfrom":"202.12.27.33"}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","querytime":4,"timestamp":1552922477.17489,"data":"tYOAAAABAAAABQAKAWUDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWbADsAWAAIAAQACowAABAFnwA7AFgACAAEAAqMAAAQBZMAOwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZMASwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsAMAAEAAQACowAABMGwkBbASgABAAEAAqMAAATABQQCwGgAAQABAAKjAAAEwgAJAcAqAAEAAQACowAABMKSai7AOgABAAEAAqMAAATCACQBwEoAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwDoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"WI04J6v9tb8oD+4bFtDPog":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"xbeAAAABAAAADQAOBnRpbm5pZQRhcmluA25ldAAAHAABwBgAAgABAAKjAAARAWQMZ3RsZC1zZXJ2ZXJzwBjAGAACAAEAAqMAAAQBaMAvwBgAAgABAAKjAAAEAWrAL8AYAAIAAQACowAABAFrwC/AGAACAAEAAqMAAAQBbcAvwBgAAgABAAKjAAAEAWfAL8AYAAIAAQACowAABAFjwC/AGAACAAEAAqMAAAQBZcAvwBgAAgABAAKjAAAEAWHAL8AYAAIAAQACowAABAFiwC/AGAACAAEAAqMAAAQBbMAvwBgAAgABAAKjAAAEAWnAL8AYAAIAAQACowAABAFmwC/AugABAAEAAqMAAATABQYewMoAAQABAAKjAAAEwCEOHsCaAAEAAQACowAABMAaXB7ALQABAAEAAqMAAATAH1AewKoAAQABAAKjAAAEwAxeHsD6AAEAAQACowAABMAjMx7AigABAAEAAqMAAATAKl0ewEoAAQABAAKjAAAEwDZwHsDqAAEAAQACowAABMArrB7AWgABAAEAAqMAAATAME8ewGoAAQABAAKjAAAEwDSyHsDaAAEAAQACowAABMApoh7AegABAAEAAqMAAATAN1MewLoAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMA==","querytime":4,"answerfrom":"202.12.27.33","timestamp":1552922479.39457}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"nkiAAAABAAAABQAKAWcDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWbADsAWAAIAAQACowAABAFkwA7AFgACAAEAAqMAAAQBZcAOwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZMASwAwAAQABAAKjAAAEwgAkAcA6AAEAAQACowAABMAFBALAaAABAAEAAqMAAATCAAkBwEoAAQABAAKjAAAEwbCQFsAqAAEAAQACowAABMKSai7ADAAcAAEAAqMAABAgAQZ4AEwAAAAAAAAAAAABwDoAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBoABwAAQACowAAECABBngADAAAAAAAAAAAAAHASgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCoAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"202.12.27.33","querytime":3,"timestamp":1552922477.53459}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"mYmAAAABAAAABQAKA25zMQNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFkA2V4dMAQwBQAAgABAAKjAAAEAWXAKsAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZMAQwBQAAgABAAKjAAAEAWbAKsAoAAEAAQACowAABMAFBALAXAABAAEAAqMAAATCAAkBwDwAAQABAAKjAAAEwbCQFsBsAAEAAQACowAABMKSai7ATAABAAEAAqMAAATCACQBwCgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHAPAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","querytime":3,"answerfrom":"202.12.27.33","timestamp":1552922478.43579}}},"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"teWAAAABAAAADQAPAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwBsAAgABAAKjAAAEAWvADsAbAAIAAQACowAABAFkwA7AGwACAAEAAqMAAAQBasAOwBsAAgABAAKjAAACwAzAGwACAAEAAqMAAAQBacAOwBsAAgABAAKjAAAEAWfADsAbAAIAAQACowAABAFowA7AGwACAAEAAqMAAAQBbcAOwBsAAgABAAKjAAAEAWbADsAbAAIAAQACowAABAFlwA7AGwACAAEAAqMAAAQBbMAOwBsAAgABAAKjAAAEAWPADsAbAAIAAQACowAABAFiwA7ADAABAAEAAqMAAATABQYewO4AAQABAAKjAAAEwCEOHsDeAAEAAQACowAABMAaXB7AQAABAAEAAqMAAATAH1AewL4AAQABAAKjAAAEwAxeHsCuAAEAAQACowAABMAjMx7AfgABAAEAAqMAAATAKl0ewI4AAQABAAKjAAAEwDZwHsBuAAEAAQACowAABMArrB7AUAABAAEAAqMAAATAME8ewDAAAQABAAKjAAAEwDSyHsDOAAEAAQACowAABMApoh7AngABAAEAAqMAAATAN1MewAwAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMMDuABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","timestamp":1552922483.17491,"querytime":4,"answerfrom":"202.12.27.33"}}},"EFDojD6JfBcmEaLwh9qkiA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922476.63803,"answerfrom":"202.12.27.33","data":"8MeAAAABAAAABQAKAWQDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZsAOwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFlwA7AFgACAAEAAqMAAAQBZMASwAwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsAMAAEAAQACowAABMAFBALAaAABAAEAAqMAAATCAAkBwFgAAQABAAKjAAAEwbCQFsA4AAEAAQACowAABMKSai7ASAABAAEAAqMAAATCACQBwGgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBYABwAAQACowAAECoADXgAAAECAZMBdgFEACLAOAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"H+yAAAABAAAABgAMAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAZAAIAAQACowAAFAFlD2luLWFkZHItc2VydmVyc8AhwBkAAgABAAKjAAAEAWLAOcAZAAIAAQACowAABAFmwDnAGQACAAEAAqMAAAQBY8A5wBkAAgABAAKjAAAEAWTAOcAZAAIAAQACowAABAFhwDnAlwABAAEAAqMAAATHtLY1wFcAAQABAAKjAAAEx/23t8B3AAEAAQACowAABMTYqQrAhwABAAEAAqMAAATICjw1wDcAAQABAAKjAAAEy3dWZcBnAAEAAQACowAABMEACQHAlwAcAAEAAqMAABAmIAA34AAAAAAAAAAAAABTwFcAHAABAAKjAAAQIAEFAACHAAAAAAAAAAAAh8B3ABwAAQACowAAECABQ/gBEAAAAAAAAAAAABDAhwAcAAEAAqMAABAgARPHcBAAAAAAAAAAAABTwDcAHAABAAKjAAAQIAEN2AAGAAAAAAAAAAABAcBnABwAAQACowAAECABBnwA4AAAAAAAAAAAAAE=","answerfrom":"202.12.27.33","querytime":3,"timestamp":1552922481.17723}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"2KeAAAABAAAABQAKA25zMQNuaWMCZnIAABwAAcAUAAIAAQACowAACAFnA2V4dMAQwBQAAgABAAKjAAAEAWTAKsAUAAIAAQACowAABAFlwCrAFAACAAEAAqMAAAQBZMAQwBQAAgABAAKjAAAEAWbAKsA8AAEAAQACowAABMAFBALAXAABAAEAAqMAAATCAAkBwEwAAQABAAKjAAAEwbCQFsBsAAEAAQACowAABMKSai7AKAABAAEAAqMAAATCACQBwDwAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHATAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8AoABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","timestamp":1552922478.54528,"querytime":4,"answerfrom":"202.12.27.33"}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"RguAAAABAAAACgAPA25zMwNuaWMCc2UAAAEAAcAUAAIAAQACowAABwF5Am5zwBTAFAACAAEAAqMAAAQBYcAqwBQAAgABAAKjAAAEAWnAKsAUAAIAAQACowAABAFiwCrAFAACAAEAAqMAAAQBZsAqwBQAAgABAAKjAAAEAWfAKsAUAAIAAQACowAABAFqwCrAFAACAAEAAqMAAAQBesAqwBQAAgABAAKjAAAEAXjAKsAUAAIAAQACowAABAFjwCrAOwABAAEAAqMAAATAJJBrwFsAAQABAAKjAAAEwCSFa8C7AAEAAQACowAABMAkh2vAawABAAEAAqMAAATARzU1wHsAAQABAAKjAAAEgu8FcsBLAAEAAQACowAABMKSahbAiwABAAEAAqMAAATH/j8BwKsAAQABAAKjAAAE1WwZBMAoAAEAAQACowAABLmfxZbAmwABAAEAAqMAAAS5n8aWwDsAHAABAAKjAAAQKgED8AAAAwEAAAAAAAAAU8BbABwAAQACowAAECABBnwlTAMBAAAAAAAAAFPAuwAcAAEAAqMAABAgAQZ8JVQDAQAAAAAAAABTwGsAHAABAAKjAAAQKgED8AAAAwUAAAAAAAAAU8B7ABwAAQACowAAECABBrAADgADAAAAAAAAAAE=","timestamp":1552922482.21737,"querytime":4,"answerfrom":"202.12.27.33"}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.94126,"querytime":3,"answerfrom":"202.12.27.33","data":"nyqAAAABAAAABQAKAWQDbmljAmZyAAAcAAHAEgACAAEAAqMAAALADMASAAIAAQACowAACAFmA2V4dMAOwBIAAgABAAKjAAAEAWXANsASAAIAAQACowAABAFnwDbAEgACAAEAAqMAAAQBZMA2wAwAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcAMAAEAAQACowAABMIACQHAaAABAAEAAqMAAATABQQCwEgAAQABAAKjAAAEwbCQFsA0AAEAAQACowAABMKSai7AWAABAAEAAqMAAATCACQBwGgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBIABwAAQACowAAECoADXgAAAECAZMBdgFEACLANAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwFgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922477.07122,"answerfrom":"202.12.27.33","data":"UmiAAAABAAAABQAKAWUDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZsAOwBYAAgABAAKjAAAEAWTAEsAWAAIAAQACowAABAFkwA7AFgACAAEAAqMAAAQBZ8AOwAwAAQABAAKjAAAEwbCQFsBYAAEAAQACowAABMAFBALASAABAAEAAqMAAATCAAkBwDgAAQABAAKjAAAEwpJqLsBoAAEAAQACowAABMIAJAHADAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwFgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBIABwAAQACowAAECABBngADAAAAAAAAAAAAAHAOAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwGgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"MySAAAABAAAADQAOA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAlAAIAAQACowAAEQFnDGd0bGQtc2VydmVyc8AlwCUAAgABAAKjAAAEAW3APMAlAAIAAQACowAABAFkwDzAJQACAAEAAqMAAAQBasA8wCUAAgABAAKjAAAEAWbAPMAlAAIAAQACowAABAFjwDzAJQACAAEAAqMAAAQBa8A8wCUAAgABAAKjAAAEAWnAPMAlAAIAAQACowAABAFowDzAJQACAAEAAqMAAAQBYcA8wCUAAgABAAKjAAAEAWXAPMAlAAIAAQACowAABAFswDzAJQACAAEAAqMAAAQBYsA8wNcAAQABAAKjAAAEwAUGHsEHAAEAAQACowAABMAhDh7AlwABAAEAAqMAAATAGlwewGcAAQABAAKjAAAEwB9QHsDnAAEAAQACowAABMAMXh7AhwABAAEAAqMAAATAIzMewDoAAQABAAKjAAAEwCpdHsDHAAEAAQACowAABMA2cB7AtwABAAEAAqMAAATAK6wewHcAAQABAAKjAAAEwDBPHsCnAAEAAQACowAABMA0sh7A9wABAAEAAqMAAATAKaIewFcAAQABAAKjAAAEwDdTHsDXABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","answerfrom":"202.12.27.33","querytime":5,"timestamp":1552922485.2935}}},"uLlSKdcUBsdWu2uNVqHtoA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"timestamp":1552922480.63155,"answerfrom":"202.12.27.33","data":"g9aAAAABAAAABQAKBnJlc29uZQx1bml2LXJlbm5lczECZnIAABwAAcAgAAIAAQACowAADAFmA2V4dANuaWPAIMAgAAIAAQACowAABAFkwDrAIAACAAEAAqMAAAQBZMA2wCAAAgABAAKjAAAEAWXANsAgAAIAAQACowAABAFnwDbAXAABAAEAAqMAAATABQQCwEwAAQABAAKjAAAEwgAJAcBsAAEAAQACowAABMGwkBbANAABAAEAAqMAAATCkmouwHwAAQABAAKjAAAEwgAkAcBcABwAAQACowAAECABBQAALgAAAAAAAAAAAALATAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAABwGwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsA0ABwAAQACowAAECABBnwQEAARAAAAAAAAAFPAfAAcAAEAAqMAABAgAQZ4AEwAAAAAAAAAAAAB"}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","querytime":3,"timestamp":1552922477.30122,"data":"CM2AAAABAAAABQAKAWYDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZcAOwBYAAgABAAKjAAAEAWTADsAWAAIAAQACowAABAFkwBLAFgACAAEAAqMAAAQBZ8AOwAwAAQABAAKjAAAEwpJqLsBIAAEAAQACowAABMAFBALAWAABAAEAAqMAAATCAAkBwDgAAQABAAKjAAAEwbCQFsBoAAEAAQACowAABMIAJAHADAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEgAHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHAOAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":3,"answerfrom":"202.12.27.33","timestamp":1552922479.74687,"data":"3RmAAAABAAAABgAMATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBgAAgABAAKjAAAUAWIPaW4tYWRkci1zZXJ2ZXJzwCDAGAACAAEAAqMAAAQBZcA4wBgAAgABAAKjAAAEAWPAOMAYAAIAAQACowAABAFhwDjAGAACAAEAAqMAAAQBZMA4wBgAAgABAAKjAAAEAWbAOMB2AAEAAQACowAABMe0tjXANgABAAEAAqMAAATH/be3wGYAAQABAAKjAAAExNipCsCGAAEAAQACowAABMgKPDXAVgABAAEAAqMAAATLd1ZlwJYAAQABAAKjAAAEwQAJAcB2ABwAAQACowAAECYgADfgAAAAAAAAAAAAAFPANgAcAAEAAqMAABAgAQUAAIcAAAAAAAAAAACHwGYAHAABAAKjAAAQIAFD+AEQAAAAAAAAAAAAEMCGABwAAQACowAAECABE8dwEAAAAAAAAAAAAFPAVgAcAAEAAqMAABAgAQ3YAAYAAAAAAAAAAAEBwJYAHAABAAKjAAAQIAEGfADgAAAAAAAAAAAAAQ=="}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.40202,"querytime":3,"answerfrom":"202.12.27.33","data":"xLSAAAABAAAABQAKAmZyAAAGAAHADAACAAEAAqMAAAwBZQNleHQDbmljwAzADAACAAEAAqMAAAQBZMAiwAwAAgABAAKjAAAEAWbAIsAMAAIAAQACowAABAFnwCLADAACAAEAAqMAAAQBZMAmwDgAAQABAAKjAAAEwAUEAsBoAAEAAQACowAABMIACQHAIAABAAEAAqMAAATBsJAWwEgAAQABAAKjAAAEwpJqLsBYAAEAAQACowAABMIAJAHAOAAcAAEAAqMAABAgAQUAAC4AAAAAAAAAAAACwGgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcAgABwAAQACowAAECoADXgAAAECAZMBdgFEACLASAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwFgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","querytime":3,"timestamp":1552922488.16663,"data":"HtqAAAABAAAABQAKCWRuc21hc3RlcgNuaWMCZnIAAAEAAcAaAAIAAQACowAACAFkA2V4dMAWwBoAAgABAAKjAAAEAWTAFsAaAAIAAQACowAABAFlwDDAGgACAAEAAqMAAAQBZ8AwwBoAAgABAAKjAAAEAWbAMMAuAAEAAQACowAABMAFBALAQgABAAEAAqMAAATCAAkBwFIAAQABAAKjAAAEwbCQFsByAAEAAQACowAABMKSai7AYgABAAEAAqMAAATCACQBwC4AHAABAAKjAAAQIAEFAAAuAAAAAAAAAAAAAsBCABwAAQACowAAECABBngADAAAAAAAAAAAAAHAUgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwHIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BiABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":4,"timestamp":1552922481.5075,"answerfrom":"202.12.27.33","data":"GuuAAAABAAAABgAMATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWYLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFiwGjATAACAAEAAqMAAAQBZMBowEwAAgABAAKjAAAEAWPAaMBMAAIAAQACowAABAFhwGjATAACAAEAAqMAAAQBZcBowLIAAQABAAKjAAAEx7S2NcCCAAEAAQACowAABMf9trbAogABAAEAAqMAAATE2KkLwJIAAQABAAKjAAAEyAdWNcDCAAEAAQACowAABMt3VmXAZgABAAEAAqMAAATBAAkCwLIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CCABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAogAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwJIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8DCABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAZgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC"}}},"JHIAnHDDut/MIJng8E/fKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"8MOAAAABAAAADQAOBnRpbm5pZQRhcmluA25ldAAAAQABwBgAAgABAAKjAAARAWsMZ3RsZC1zZXJ2ZXJzwBjAGAACAAEAAqMAAAQBY8AvwBgAAgABAAKjAAAEAWnAL8AYAAIAAQACowAABAFqwC/AGAACAAEAAqMAAAQBZ8AvwBgAAgABAAKjAAAEAWTAL8AYAAIAAQACowAABAFtwC/AGAACAAEAAqMAAAQBYsAvwBgAAgABAAKjAAAEAWbAL8AYAAIAAQACowAABAFswC/AGAACAAEAAqMAAAQBZcAvwBgAAgABAAKjAAAEAWjAL8AYAAIAAQACowAABAFhwC/A+gABAAEAAqMAAATABQYewKoAAQABAAKjAAAEwCEOHsBKAAEAAQACowAABMAaXB7AigABAAEAAqMAAATAH1AewNoAAQABAAKjAAAEwAxeHsC6AAEAAQACowAABMAjMx7AegABAAEAAqMAAATAKl0ewOoAAQABAAKjAAAEwDZwHsBaAAEAAQACowAABMArrB7AagABAAEAAqMAAATAME8ewC0AAQABAAKjAAAEwDSyHsDKAAEAAQACowAABMApoh7AmgABAAEAAqMAAATAN1MewPoAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMA==","answerfrom":"202.12.27.33","querytime":4,"timestamp":1552922479.22408}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"PXuAAAABAAAADQAOAXoEYXJpbgNuZXQAAAEAAcATAAIAAQACowAAEQFlDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWzAKsATAAIAAQACowAABAFmwCrAEwACAAEAAqMAAAQBZ8AqwBMAAgABAAKjAAAEAW3AKsATAAIAAQACowAABAFowCrAEwACAAEAAqMAAAQBYsAqwBMAAgABAAKjAAAEAWPAKsATAAIAAQACowAABAFhwCrAEwACAAEAAqMAAAQBacAqwBMAAgABAAKjAAAEAWTAKsATAAIAAQACowAABAFrwCrAEwACAAEAAqMAAAQBasAqwLUAAQABAAKjAAAEwAUGHsCVAAEAAQACowAABMAhDh7ApQABAAEAAqMAAATAGlwewNUAAQABAAKjAAAEwB9QHsAoAAEAAQACowAABMAMXh7AVQABAAEAAqMAAATAIzMewGUAAQABAAKjAAAEwCpdHsCFAAEAAQACowAABMA2cB7AxQABAAEAAqMAAATAK6wewPUAAQABAAKjAAAEwDBPHsDlAAEAAQACowAABMA0sh7ARQABAAEAAqMAAATAKaIewHUAAQABAAKjAAAEwDdTHsC1ABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1552922479.80248,"querytime":4,"answerfrom":"202.12.27.33"}}}} -d.ext.nic.fr 192.5.4.2 {"anMXpIkCCaKzuyWsrPPtLw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.72514,"querytime":13,"answerfrom":"192.5.4.2","data":"GnOEAAABAAUAAAAAAmZyAAACAAHADAACAAEAAqMAAAgBZANuaWPADMAMAAIAAQACowAACAFmA2V4dMAiwAwAAgABAAKjAAAEAWXANsAMAAIAAQACowAABAFkwDbADAACAAEAAqMAAAQBZ8A2"}}}} -d.ext.nic.fr 2001:0500:002e:0000:0000:0000:0000:0002 {} -f.ip6-servers.arpa 193.0.9.2 {"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"qGWAAAABAAAABgAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAAMA25zNAVhcG5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAAEAZzbnMtcGIDaXNjA29yZwDAQAACAAEAAVGAAA4GdGlubmllBGFyaW7AcQ==","answerfrom":"193.0.9.2","querytime":9,"timestamp":1552922480.87062}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"k4uAAAABAAAABgAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAAEAZzbnMtcGIDaXNjA29yZwDAQAACAAEAAVGAAA4GdGlubmllBGFyaW7AccBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAADANuczQFYXBuaWPAcQ==","answerfrom":"193.0.9.2","querytime":10,"timestamp":1552922479.17736}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"193.0.9.2","querytime":9,"timestamp":1552922481.54402,"data":"ZUmAAAABAAAABgAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEAAAgABAAFRgAAQA25zMwZsYWNuaWMDbmV0AMBAAAIAAQABUYAADgNuczMHYWZyaW5pY8BxwEAAAgABAAFRgAAMA25zNAVhcG5pY8BxwEAAAgABAAFRgAATA3ByaQdhdXRoZG5zBHJpcGXAccBAAAIAAQABUYAAEAZzbnMtcGIDaXNjA29yZwDAQAACAAEAAVGAAA4GdGlubmllBGFyaW7AcQ=="}}}} -f.ip6-servers.arpa 2001:067c:00e0:0000:0000:0000:0000:0002 {"DopGLtTlD8qdrSAvQzI7OA":null,"gNvppsjyN6Isa7C+Jawn6w":null,"dVKTTf6INn/t4Xg/r5toXA":null} -resone.univ-rennes1.fr 129.20.254.1 {"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"b/GAAAABAAAAAwAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHADgACAAEAAVGAAAwDbnMxA25pYwJmcgDADgACAAEAAVGAAAYDbnMywDnADgACAAEAAVGAAAYDbnMzwDk=","answerfrom":"129.20.254.1","querytime":10,"timestamp":1552922480.69413}}}} -e.gtld-servers.net 192.12.94.30 {} -l.root-servers.net 2001:0500:0003:0000:0000:0000:0000:0042 {} +e.ip6-servers.arpa 203.119.86.101 {} +e.ip6-servers.arpa 2001:0dd8:0006:0000:0000:0000:0000:0101 {} +e.in-addr-servers.arpa 2001:0dd8:0006:0000:0000:0000:0000:0101 {} +e.in-addr-servers.arpa 203.119.86.101 {} l.root-servers.net 199.7.83.42 {} -tinnie.arin.net 199.212.0.53 {"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"maCAAAABAAAABAAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDwAAgABAAKjAAAQA25zMgdyZW5hdGVyAmZyAMA8AAIAAQACowAACgNuczMDbmljwHLAPAACAAEAAqMAAAYDbnMxwGrAPAACAAEAAqMAAAwEaW1hZwRpbWFnwHI=","querytime":80,"answerfrom":"199.212.0.53","timestamp":1552922480.96143}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"nOSAAAABAAAAAwAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDQAAgABAAKjAAAMA25zMQNuaWMCZnIAwDQAAgABAAKjAAAGA25zMsBqwDQAAgABAAKjAAAGA25zM8Bq","timestamp":1552922479.53426,"querytime":79,"answerfrom":"199.212.0.53"}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"8HuAAAABAAAABAAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDwAAgABAAKjAAAMA25zMwNuaWMCZnIAwDwAAgABAAKjAAAMBGltYWcEaW1hZ8BuwDwAAgABAAKjAAAOA25zMgdyZW5hdGVywG7APAACAAEAAqMAAAYDbnMxwJo=","querytime":79,"answerfrom":"199.212.0.53","timestamp":1552922481.63687}}}} -u.arin.net 204.61.216.50 {"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922480.08514,"answerfrom":"204.61.216.50","data":"Dw2EAAABAAEABAAIAXoEYXJpbgNuZXQAABwAAcAMABwAAQAAqMAAECABBQAAEwAAAAAAAAAAAGPADgACAAEAAKjAAAYDbnMywA7ADgACAAEAAKjAAAYDbnMzwA7ADgACAAEAAKjAAAQBdcAOwA4AAgABAACowAAGA25zMcAOwGgAAQABAACowAAEzD3YMsB4AAEAAQAAqMAABMfUAGzARAABAAEAAKjAAATHRwBswFYAAQABAACowAAExwUabMBoABwAAQAAqMAAECABBQAAFGBQAK0AAAAAAAHAeAAcAAEAAKjAABAgAQUAABMAAAAAAAAAAAEIwEQAHAABAACowAAQIAEFAAAxAAAAAAAAAAABCMBWABwAAQAAqMAAECABBQAAqQAAAAAAAAAAAQg="}}},"JHIAnHDDut/MIJng8E/fKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Yf2EAAABAAEABAAIBnRpbm5pZQRhcmluA25ldAAAAQABwAwAAQABAACowAAEx9QANcATAAIAAQAAqMAABAF1wBPAEwACAAEAAKjAAAYDbnMzwBPAEwACAAEAAKjAAAYDbnMxwBPAEwACAAEAAKjAAAYDbnMywBPAPQABAAEAAKjAAATMPdgywF8AAQABAACowAAEx9QAbMBxAAEAAQAAqMAABMdHAGzATQABAAEAAKjAAATHBRpswD0AHAABAACowAAQIAEFAAAUYFAArQAAAAAAAcBfABwAAQAAqMAAECABBQAAEwAAAAAAAAAAAQjAcQAcAAEAAKjAABAgAQUAADEAAAAAAAAAAAEIwE0AHAABAACowAAQIAEFAACpAAAAAAAAAAABCA==","timestamp":1552922479.35344,"querytime":10,"answerfrom":"204.61.216.50"}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922479.92146,"answerfrom":"204.61.216.50","data":"r3uEAAABAAEABAAIAXoEYXJpbgNuZXQAAAEAAcAMAAEAAQAAqMAABMfUAD/ADgACAAEAAKjAAAYDbnMzwA7ADgACAAEAAKjAAAYDbnMywA7ADgACAAEAAKjAAAQBdcAOwA4AAgABAACowAAGA25zMcAOwFwAAQABAACowAAEzD3YMsBsAAEAAQAAqMAABMfUAGzASgABAAEAAKjAAATHRwBswDgAAQABAACowAAExwUabMBcABwAAQAAqMAAECABBQAAFGBQAK0AAAAAAAHAbAAcAAEAAKjAABAgAQUAABMAAAAAAAAAAAEIwEoAHAABAACowAAQIAEFAAAxAAAAAAAAAAABCMA4ABwAAQAAqMAAECABBQAAqQAAAAAAAAAAAQg="}}},"WI04J6v9tb8oD+4bFtDPog":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"204.61.216.50","querytime":10,"timestamp":1552922479.50249,"data":"dr+EAAABAAEABAAIBnRpbm5pZQRhcmluA25ldAAAHAABwAwAHAABAACowAAQIAEFAAATAAAAAAAAx9QANcATAAIAAQAAqMAABAF1wBPAEwACAAEAAKjAAAYDbnMxwBPAEwACAAEAAKjAAAYDbnMywBPAEwACAAEAAKjAAAYDbnMzwBPASQABAAEAAKjAAATMPdgywFkAAQABAACowAAEx9QAbMBrAAEAAQAAqMAABMdHAGzAfQABAAEAAKjAAATHBRpswEkAHAABAACowAAQIAEFAAAUYFAArQAAAAAAAcBZABwAAQAAqMAAECABBQAAEwAAAAAAAAAAAQjAawAcAAEAAKjAABAgAQUAADEAAAAAAAAAAAEIwH0AHAABAACowAAQIAEFAACpAAAAAAAAAAABCA=="}}}} -u.arin.net 2001:0500:0014:6050:00ad:0000:0000:0001 {} -ns1.asnlookup.zonemaster.net 2a00:0801:00f0:0106:0000:0000:0000:0052 {} -ns1.asnlookup.zonemaster.net 91.226.36.52 {"IKQNXmg3BtKJAYq9clrK6g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7vuEAAABAAIAAgACATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgdvcmlnaW42CWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAEAABwAwAEAABAAA4QAA2NTI0ODUgfCAyMDAxOjY3YzoyMjE4OjovNDcgfCBGUiB8IHJpcGVuY2MgfCAyMDExLTA0LTI4wAwAEAABAAA4QAA2NTI0ODUgfCAyMDAxOjY3YzoyMjE4OjovNDggfCBGUiB8IHJpcGVuY2MgfCAyMDExLTA0LTI4wFQAAgABAAFRgAAGA25zMcBUwFQAAgABAAFRgAAGA25zMsBUwRQAAQABAAFRgAAEW+IkNcECAAEAAQABUYAABFviJDQ=","querytime":57,"answerfrom":"91.226.36.52","timestamp":1552922485.94638}}},"5BrASPi6wmwbUi8r1+EWyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"j5SEAAABAAIAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAAgABwAwAAgABAAFRgAAGA25zMsAMwAwAAgABAAFRgAAGA25zMcAMwAwAAgABAAFRgAACwEjADAACAAEAAVGAAALANsA2AAEAAQABUYAABFviJDXASAABAAEAAVGAAARb4iQ0","querytime":54,"timestamp":1552922485.62271,"answerfrom":"91.226.36.52"}}},"ldhoYrQfMMgR0AUdKtd9qw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"0MOEAAABAAIAAgACATEBNAMxMzQDMTkyBm9yaWdpbglhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABAAAcAMABAAAQAAOEAAMjEyNDg1IHwgMTkyLjEzNC40LjAvMjIgfCBGUiB8IHJpcGVuY2MgfCAxOTkzLTA0LTIxwAwAEAABAAA4QAAyMTIyMDAgfCAxOTIuMTM0LjAuMC8xNyB8IEZSIHwgcmlwZW5jYyB8IDE5OTMtMDQtMjHAHwACAAEAAVGAAAYDbnMywB/AHwACAAEAAVGAAAYDbnMxwB/AxQABAAEAAVGAAARb4iQ1wNcAAQABAAFRgAAEW+IkNA==","answerfrom":"91.226.36.52","querytime":54,"timestamp":1552922485.87278}}}} -e.ext.nic.fr 2a00:0d78:0000:0102:0193:0176:0144:0022 {} -e.ext.nic.fr 193.176.144.22 {} -k.gtld-servers.net 192.52.178.30 {} -x.ns.se 213.108.25.4 {} -b.root-servers.net 192.228.79.201 {} -c.ip6-servers.arpa 2001:43f8:0110:0000:0000:0000:0000:0011 {} -c.ip6-servers.arpa 196.216.169.11 {} -av3.nstld.com 2001:0500:0126:0000:0000:0000:0000:0030 {} -av3.nstld.com 192.82.133.30 {} -av2.nstld.com 192.42.178.30 {} -av2.nstld.com 2001:0500:0125:0000:0000:0000:0000:0030 {} -ns1.ext.nic.fr 193.51.208.13 {} +l.root-servers.net 2001:0500:009f:0000:0000:0000:0000:0042 {} d.ip6-servers.arpa 2001:13c7:7012:0000:0000:0000:0000:0053 {} d.ip6-servers.arpa 200.7.86.53 {} -i.ns.se 194.146.106.22 {} -a.in-addr-servers.arpa 2620:0037:e000:0000:0000:0000:0000:0053 {} -a.in-addr-servers.arpa 199.180.182.53 {} -i.gtld-servers.net 192.43.172.30 {} -ns3.nic.fr 2001:0660:3006:0001:0000:0000:0001:0001 {"pOu0bQGV1Z/qItEzagW7FA":null,"4YeoSYuQtm/OKn1ycfuP2Q":null,"TJ/AJOD5oSy8le0E7lFXTg":null,"auoc0YjagPcFkyMHmxRk1g":null,"deQZZWplsy7gFo3gqzBFcg":null,"UzfnoYKACE71u6V/QQ17nw":null,"zueeZkdHfWptXkiyz6gmOA":null,"KsaIpCVhpeMsuVQal+DZhQ":null,"cgyeMZwmsWADV4zvkMbarg":null,"F8IuiH+hJkzDv9m0t6ioyA":null,"Y/FppjiF5p7LpFx+5iKNTQ":null,"u2IVdHOj+hP1WgRzMMG0Hg":null,"CFtsJvzdNZr5xrBiWkborQ":null,"G5mY7b4q1l50Mf6YfNGUtg":null,"2/fg0ozhiGSsmWtVHzHJow":null,"nys7lrpg8L2EaHBxy2MfKg":null} -ns3.nic.fr 192.134.0.49 {"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"DYGEAAABAAEAAwAGBWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgAMAAAAAAAAAAlwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczHARsBqAAEAAQACowAABMCGBAHAQgABAAEAAqMAAATAXQAEwFgAAQABAAKjAAAEwIYAMcBqABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAQgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwFgAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAQ==","timestamp":1552922487.27758,"querytime":18,"answerfrom":"192.134.0.49"}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922481.45471,"data":"gqyEAAABAAEAAwAGAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAMAAwAAQACowAADANuczMDbmljAmZyAMAPAAIAAQACowAABgNuczHAO8APAAIAAQACowAAAsA3wA8AAgABAAKjAAAGA25zMsA7wE8AAQABAAKjAAAEwIYEAcBvAAEAAQACowAABMBdAATANwABAAEAAqMAAATAhgAxwE8AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBvABwAAQACowAAECABBmAwBQABAAAAAAABAALANwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922480.7839,"data":"KmWEAAABAAEAAwAGATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHADAAMAAEAAqMAAAwDbnMyA25pYwJmcgDADgACAAEAAqMAAAYDbnMxwDnADgACAAEAAqMAAALANcAOAAIAAQACowAABgNuczPAOcBNAAEAAQACowAABMCGBAHANQABAAEAAqMAAATAXQAEwG0AAQABAAKjAAAEwIYAMcBNABwAAQACowAAECABBnwiGAACAAAAAAAEAAHANQAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwG0AHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAQ=="}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"answerfrom":"192.134.0.49","timestamp":1552922486.85213,"data":"VNWABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ=="}}},"Y/FppjiF5p7LpFx+5iKNTQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922487.4772,"data":"r9OEAAABAAEAAQAAA3d3VwVhRm5JYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922480.27319,"data":"iLiEAAABAAEAAwAGATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwAwADAABAAKjAAAMA25zMQNuaWMCZnIAwA4AAgABAAKjAAACwDbADgACAAEAAqMAAAYDbnMzwDrADgACAAEAAqMAAAYDbnMywDrANgABAAEAAqMAAATAhgQBwG4AAQABAAKjAAAEwF0ABMBcAAEAAQACowAABMCGADHANgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwG4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBcABwAAQACowAAECABBmAwBgABAAAAAAABAAE="}}},"deQZZWplsy7gFo3gqzBFcg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"answerfrom":"192.134.0.49","timestamp":1552922487.63619,"data":"h++EAAABAAEAAQAAA1dXVwVhZk5JQwJmcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"LoaEAAABAAEAAwAGATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMQNuaWMCZnIAwDQAAgABAAKjAAAGA25zMsBqwDQAAgABAAKjAAAGA25zM8BqwDQAAgABAAKjAAACwGbAZgABAAEAAqMAAATAhgQBwH4AAQABAAKjAAAEwF0ABMCQAAEAAQACowAABMCGADHAZgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwH4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsCQABwAAQACowAAECABBmAwBgABAAAAAAABAAE=","querytime":19,"timestamp":1552922479.69512,"answerfrom":"192.134.0.49"}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"K7mEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwIcAAQABAAKjAAAEwIYEAcB1AAEAAQACowAABMBdAATAYwABAAEAAqMAAATAhgAxwIcAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcB1ABwAAQACowAAECABBmAwBQABAAAAAAABAALAYwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB","answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922476.36308}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"NMaEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922487.0489}}},"P5BT4xLKPc0cvgR8IR22ww":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"4GCEAAABAAEAAQAAA1dXdwVhZm5pQwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":18,"timestamp":1552922501.51818,"answerfrom":"192.134.0.49"}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"aGeEAAABAAEAAwAGATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMwNuaWMCZnIAwDQAAgABAAKjAAAGA25zMcBqwDQAAgABAAKjAAAGA25zMsBqwDQAAgABAAKjAAACwGbAfgABAAEAAqMAAATAhgQBwJAAAQABAAKjAAAEwF0ABMBmAAEAAQACowAABMCGADHAfgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwJAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBmABwAAQACowAAECABBmAwBgABAAAAAAABAAE=","querytime":19,"timestamp":1552922481.80037,"answerfrom":"192.134.0.49"}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":37,"timestamp":1552922481.99083,"data":"RE6EAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwHUAAQABAAKjAAAEwIYEAcBjAAEAAQACowAABMBdAATAhwABAAEAAqMAAATAhgAxwHUAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBjABwAAQACowAAECABBmAwBQABAAAAAAABAALAhwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922487.85714}}},"0TlRUFu34T/7a+Z9BxkQVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"answerfrom":"192.134.0.49","timestamp":1552922501.41494,"data":"9rOEAAABAAEAAQAAA1dXdwVBRm5pYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922487.77555,"querytime":18,"answerfrom":"192.134.0.49","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"aU2EAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAQgBAQMIAwEAAdPH3dD6UIEkcVMThgn7fUgbxw8GJfXbWXrpCc7J8hR5EsNdZ7whc6oqa3ziZetJecJ318KqIT75I/yDwp3FCk6936tTdnntmDwl5CNn0F16OOtP4kZOUDs2hwWEcyz5sUPPs5v5Vuoo6/piPBXh6pS+J5AAqzLIAK11sXU67ORDVdHXIRV8VDGne3p+jV/PmSzgdAZfJ5xUxMuV76DrlWZkKIn/658Zl+Zat0y85FrsmXPYMG/8Y/l0J0jtqgaTUIRVM2ZryoKDH98UH8tmKfXK87eqKLV4RHvd/wXBgv65d1+JOrf2R3J1LQNhsyAdlED2e111VLKDgxcjc66HOPfADAAwAAEAAqMAAQgBAAMIAwEAAaqBT2ZQrM4q3xjx++/rY6vXzxam2pImOAYONiOM9xfyPH2XmBPU9ghseSe/idBMyjoLP7S017wFZymo+45PAWTSPsPUJrf2s8kOXVsuSFBr6sM2dQM3dnDRcXdGcLUrJksXrI7QGVSOFWKOcYPe1xzEbf2VP68I2sWaaMp7QMkDADew7nFxV/dP1RCMhMJ6y575TvOg7z/L1WV1d0fyxZ4miftL6z/5+u2KWDdXISUSSa/HcRSQAWtbHgWds+34lQMmXlrDWI3sq/kkWr+KDJ/M01+3cX94rGGEPAp1x4ZJiwdzLsvBTC9z8cgVpGgeAF/oxN55nL3EAvFs8AhomkPADAAwAAEAAqMAAQgBAAMIAwEAAdPrdwx/CIhOZf01Mv8PA3X3i2VzrrbC8fPXWpSMy6ki0QvyLCq6ZUf4lcPCd0VucqTztRpyrQqhYSfDBUC2tTgAbfNtL/icoWKO0QNyPlilWlsNE5mvQ0cqORRNX0V2AZTReComqVivpe81tgJFL8HMFxqRtcIA1ngo2i4atZ5STm05dBXZzlPxpFvwefHRZW3VfF3wudskg8PHCLvfOhuSie/tWEd2iBfba80i8pe96iZGJZ5YZFTf9kZeWcCC5E6pZtDstlUTyII0DZxo2OZ42YBUR4pzAcCLU6wGodZLGJcPXOb/wS1BmzNI3r+OaKr8N/0YD8xJuQEmJ/dN64fADAAuAAEAAqMAARwAMAgCAAKjAFyxUIBciZEWkuoFYWZuaWMCZnIAT0EnEMVHTIAnBuuSwkCZDNXAkpMDT0E60KXi5iaiqcWyXXOW9b/yXEBFKlpG72kp9v2qtBzpPsZHafk+7A4B6BomBEoM8SuDCqt6Tdz/apNXp3mc3sewL9jF56ALPGolozmIEFZmstBUn4NcBxntRTzxH+GyH5UB9Jn4TJVq+wGVUx6QYdg9L8uAjkcP3jBQAb8L1czmemiF2r1MMbTCPYRebdK5gQFhdgREOXwj2mc8RjcPxuud/CM01vxnQwYobCKt+deeropJi//JN13RPGJ2siexMxe8IOlmhq9Qfzoik2OwwPtfQTIVHdRcw0KbiDLAGGoNLKCmBcsDQxnbeQAAKQWYAACAAAAA","answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922486.48554}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922486.87805,"data":"XE6ABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ=="}}},"MgH4T3MlBDXU0qaxaRcnAA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"E0uEAAABAAEAAQAAA3dXVwVhRk5pYwJmUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922501.54455}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"timestamp":1552922486.90423,"answerfrom":"192.134.0.49","data":"NdOABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB"}}},"KsaIpCVhpeMsuVQal+DZhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"gxKEAAABAAEAAQAAA3dXVwVhZk5pQwJGUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":18,"answerfrom":"192.134.0.49","timestamp":1552922487.60972}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"answerfrom":"192.134.0.49","timestamp":1552922487.69414,"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA=="}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","querytime":18,"timestamp":1552922487.38337,"data":"Z46ABQABAAAAAAAAAAACAAE="}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"o0SEAAABAAEAAwAGATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMgNuaWMCZnIAwDQAAgABAAKjAAAGA25zMcBqwDQAAgABAAKjAAAGA25zM8BqwDQAAgABAAKjAAACwGbAfgABAAEAAqMAAATAhgQBwGYAAQABAAKjAAAEwF0ABMCQAAEAAQACowAABMCGADHAfgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGYAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsCQABwAAQACowAAECABBmAwBgABAAAAAAABAAE=","answerfrom":"192.134.0.49","querytime":19,"timestamp":1552922481.12347}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"timestamp":1552922486.15221,"answerfrom":"192.134.0.49","data":"tTyEAAABAAMAAAAGBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zM8AqwCYAAQABAAKjAAAEwIYEAcA8AAEAAQACowAABMBdAATATgABAAEAAqMAAATAhgAxwCYAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcA8ABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}}} -f.ext.nic.fr 2001:067c:1010:0011:0000:0000:0000:0053 {} -f.ext.nic.fr 194.146.106.46 {} -d.gtld-servers.net 192.31.80.30 {} -ns3.nic.se 91.226.37.45 {"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"91.226.37.45","querytime":53,"timestamp":1552922482.91241,"data":"8J+EAAABAAEAAwAECnpvbmVtYXN0ZXIDbmV0AAAGAAHADAAGAAEAAA4QADACbnMDbmljAnNlAApob3N0bWFzdGVyA2lpc8AzXI5t1QAAKjAAAA4QAAk6gAAADhDADAACAAEAAA4QAAwDbnMyA25pYwJmcgDADAACAAEAAA4QAALALMAMAAIAAQAADhAABgNuczPAL8AsAAEAAQAADhAABFviJC3ALAAcAAEAAA4QABAgAQZ8EkwQCgAAAAAAAABFwI4AAQABAAAOEAAEW+IlLcCOABwAAQAADhAAECABBnwSTCAHAAAAAAAAAEU="}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":53,"timestamp":1552922485.1324,"answerfrom":"91.226.37.45","data":"YGOAAAABAAAAAgAEA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAQAAIAAQAADhAABgNuczLAEMAQAAIAAQAADhAAAsAMwAwAAQABAAAOEAAEW+IkNMAMABwAAQAADhAAECoACAEA8AEGAAAAAAAAAFLAOgABAAEAAA4QAARb4iQ1wDoAHAABAAAOEAAQKgAIAQDwAQYAAAAAAAAAUw=="}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":53,"answerfrom":"91.226.37.45","timestamp":1552922482.47747,"data":"eMeEAAABAAEAAwADA25zMwNuaWMCc2UAABwAAcAMABwAAQAADhAAECABBnwSTCAHAAAAAAAAAEXAEAACAAEAAA4QAAUCbnPAEMAQAAIAAQAADhAAAsAMwBAAAgABAAAOEAAHAWkCbnPAFMBEAAEAAQAADhAABFviJC3ARAAcAAEAAA4QABAgAQZ8EkwQCgAAAAAAAABFwAwAAQABAAAOEAAEW+IlLQ=="}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"6XaEAAABAAEAAwADAm5zA25pYwJzZQAAHAABwAwAHAABAAAOEAAQIAEGfBJMEAoAAAAAAAAARcAPAAIAAQAADhAABgNuczPAD8APAAIAAQAADhAAAsAMwA8AAgABAAAOEAAHAWkCbnPAE8AMAAEAAQAADhAABFviJC3AQwABAAEAAA4QAARb4iUtwEMAHAABAAAOEAAQIAEGfBJMIAcAAAAAAAAARQ==","querytime":54,"timestamp":1552922484.62695,"answerfrom":"91.226.37.45"}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"91.226.37.45","querytime":53,"timestamp":1552922482.31417,"data":"vauEAAABAAEAAwADA25zMwNuaWMCc2UAAAEAAcAMAAEAAQAADhAABFviJS3AEAACAAEAAA4QAAcBaQJuc8AUwBAAAgABAAAOEAACwAzAEAACAAEAAA4QAAUCbnPAEMBZAAEAAQAADhAABFviJC3AWQAcAAEAAA4QABAgAQZ8EkwQCgAAAAAAAABFwAwAHAABAAAOEAAQIAEGfBJMIAcAAAAAAAAARQ=="}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":53,"timestamp":1552922485.4746,"answerfrom":"91.226.37.45","data":"m/GAAAABAAAAAgAEA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAQAAIAAQAADhAAAsAMwBAAAgABAAAOEAAGA25zMsAQwAwAHAABAAAOEAAQKgAIAQDwAQYAAAAAAAAAUsAMAAEAAQAADhAABFviJDTASAABAAEAAA4QAARb4iQ1wEgAHAABAAAOEAAQKgAIAQDwAQYAAAAAAAAAUw=="}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922482.56043,"querytime":53,"answerfrom":"91.226.37.45","data":"L4mAAAABAAAAAgAECWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwAwAAgABAAAOEAAGA25zMsAMwAwAAgABAAAOEAAGA25zMcAMwEgAAQABAAAOEAAEW+IkNMBIABwAAQAADhAAECoACAEA8AEGAAAAAAAAAFLANgABAAEAAA4QAARb4iQ1wDYAHAABAAAOEAAQKgAIAQDwAQYAAAAAAAAAUw=="}}},"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"bCaEAAABAAEAAwADAm5zA25pYwJzZQAAAQABwAwAAQABAAAOEAAEW+IkLcAPAAIAAQAADhAABgNuczPAD8APAAIAAQAADhAAAsAMwA8AAgABAAAOEAAHAWkCbnPAE8AMABwAAQAADhAAECABBnwSTBAKAAAAAAAAAEXANwABAAEAAA4QAARb4iUtwDcAHAABAAAOEAAQIAEGfBJMIAcAAAAAAAAARQ==","querytime":53,"answerfrom":"91.226.37.45","timestamp":1552922484.46293}}}} -ns3.nic.se 2001:067c:124c:2007:0000:0000:0000:0045 {"zQS3R7z6GBeKGH+IUB62IQ":null,"fxPbDm0VpYQV73LLw/20YA":null,"qa2ldaRH3Yi2ZSb8/9xaSw":null,"nLqvsXvTOBR/25TgODnu7g":null} -ns3.arin.net 199.5.26.108 {} +c.root-servers.net 2001:0500:0002:0000:0000:0000:0000:000c {} +c.root-servers.net 192.33.4.12 {} +f.ext.nic.fr 2001:067c:1010:0011:0000:0000:0000:0053 {"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":9,"timestamp":1626875447.14752,"data":"dKOAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczbAOsAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAcAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwEwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBeABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEgjsfHcBMAAEAAQACowAABMCGADHAXgABAAEAAqMAAATAXQAEwDYAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:67c:1010:11::53"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.65538,"querytime":8,"answerfrom":"2001:67c:1010:11::53","data":"DXmAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAGA25zMcAQwBAAAgABAAKjAAAGA25zM8AQwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbAXsAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAcAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwEgAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcA2ABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEgjsfHcBIAAEAAQACowAABMCGADHANgABAAEAAqMAAATAhgQBwFoAAQABAAKjAAAEwTPQDQ=="}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":9,"timestamp":1626875446.89776,"data":"wRmAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbAXsAMAAEAAQACowAABMCGBAHADAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8A6ABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHAAAQABAAKjAAAEgjsfHcA6AAEAAQACowAABMCGADHAKAABAAEAAqMAAATAXQAEwFoAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:67c:1010:11::53"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"dpeAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbAXsAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8A6ABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwHAAAQABAAKjAAAEgjsfHcA6AAEAAQACowAABMCGADHAKAABAAEAAqMAAATAhgQBwFoAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:67c:1010:11::53","querytime":9,"timestamp":1626875447.40512}}}} +f.ext.nic.fr 194.146.106.46 {"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"194.146.106.46","data":"Uu6AAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnM2wD7AEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAMAAEAAQACowAABMCGBAHAUAABAAEAAqMAAASCOx8dwHAAAQABAAKjAAAEwIYAMcAoAAEAAQACowAABMBdAATAOgABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBQABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AcAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwCgAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg==","timestamp":1626875446.88358,"querytime":7}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"jIuAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczHALMAQAAIAAQACowAABgNuczHAEMAMAAEAAQACowAABMBdAATAKAABAAEAAqMAAASCOx8dwEwAAQABAAKjAAAEwIYAMcBwAAEAAQACowAABMCGBAHAXgABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsAoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/ATAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwHAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.146.106.46","querytime":8,"timestamp":1626875447.39192}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":8,"timestamp":1626875447.13321,"data":"3uGAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMywBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczbAPsAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEgjsfHcAoAAEAAQACowAABMCGADHAUAABAAEAAqMAAATAXQAEwDoAAQABAAKjAAAEwTPQDcBwABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AKAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwFAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg==","answerfrom":"194.146.106.46"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":8,"timestamp":1626875447.64204,"data":"H0eAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczHALMAQAAIAAQACowAABgNuczPAEMAMABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAXQAEwCgAAQABAAKjAAAEgjsfHcBwAAEAAQACowAABMCGADHATAABAAEAAqMAAATAhgQBwF4AAQABAAKjAAAEwTPQDcAoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AcAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwEwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.146.106.46"}}}} +g.ext.nic.fr 194.0.36.1 {"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875467.70749,"querytime":29,"answerfrom":"194.0.36.1","data":"xW2AAAABAAAABQAJAWQDbmljAmZyAAABAAHADgACAAEAAqMAAAoDbnM2A2V4dMAOwA4AAgABAAKjAAAGA25zM8AOwA4AAgABAAKjAAAGA25zMcAqwA4AAgABAAKjAAAGA25zMcAOwA4AAgABAAKjAAAGA25zMsAOwCYAAQABAAKjAAAEgjsfHcA8AAEAAQACowAABMCGADHAcgABAAEAAqMAAATAXQAEwGAAAQABAAKjAAAEwIYEAcBOAAEAAQACowAABMEz0A3AJgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwDwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcByABwAAQACowAAECABBmAwBQABAAAAAAABAALAYAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB"}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"194.0.36.1","data":"HWuAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zM8ASwDwAAQABAAKjAAAEgjsfHcByAAEAAQACowAABMCGADHATgABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYEAcBgAAEAAQACowAABMEz0A3APAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwHIAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBOABwAAQACowAAECABBmAwBQABAAAAAAABAALAKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","timestamp":1626875471.12277,"querytime":29}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"/QiAAAABAAAABQAJA25zMwNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczHALMAMAAEAAQACowAABMCGADHAKAABAAEAAqMAAASCOx8dwF4AAQABAAKjAAAEwF0ABMBMAAEAAQACowAABMCGBAHAcAABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AXgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwEwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875468.6332}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875467.59275,"querytime":29,"answerfrom":"194.0.36.1","data":"coWEAAABAAEABAAAAmZyAAAGAAHADAAGAAEAAqMAADAIbnNtYXN0ZXIDbmljwAwKaG9zdG1hc3RlcsAphMa+GwAADhAAAAcIADbugAAAFRjADAACAAEAAqMAAAgBZgNleHTAKcAMAAIAAQACowAABAFnwF7ADAACAAEAAqMAAAQBZMApwAwAAgABAAKjAAAEAWXAXg=="}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":29,"timestamp":1626875470.95984,"data":"kwSAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMsASwDwAAQABAAKjAAAEgjsfHcBOAAEAAQACowAABMCGADHAcgABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYEAcBgAAEAAQACowAABMEz0A3APAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwE4AHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcByABwAAQACowAAECABBmAwBQABAAAAAAABAALAKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","answerfrom":"194.0.36.1"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875447.16147,"querytime":30,"answerfrom":"194.0.36.1","data":"q6KAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczbAUMAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEgjsfHcA6AAEAAQACowAABMCGADHAKAABAAEAAqMAAATAXQAEwEwAAQABAAKjAAAEwTPQDcBwABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AOgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwCgAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg=="}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":31,"timestamp":1626875447.66938,"data":"2dKAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczHALMAQAAIAAQACowAABgNuczPAEMAMABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAXQAEwCgAAQABAAKjAAAEgjsfHcBwAAEAAQACowAABMCGADHATAABAAEAAqMAAATAhgQBwF4AAQABAAKjAAAEwTPQDcAoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AcAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwEwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.36.1"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"iJOAAAABAAAABQAJA25zMwNuaWMCZnIAABwAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAGA25zMsAQwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbATMAQAAIAAQACowAABgNuczHAEMAMABwAAQACowAAECABBmAwBgABAAAAAAABAAHADAABAAEAAqMAAATAhgAxwF4AAQABAAKjAAAEgjsfHcA2AAEAAQACowAABMBdAATAcAABAAEAAqMAAATAhgQBwEgAAQABAAKjAAAEwTPQDcBeABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/ANgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875468.78862}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"194.0.36.1","data":"ZeyAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zM8ASwGAAAQABAAKjAAAEgjsfHcByAAEAAQACowAABMCGADHAPAABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYEAcBOAAEAAQACowAABMEz0A3AYAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwHIAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcA8ABwAAQACowAAECABBmAwBQABAAAAAAABAALAKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","timestamp":1626875471.46105,"querytime":29}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":30,"timestamp":1626875470.79308,"data":"PDCAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwCoAAQABAAKjAAAEgjsfHcA8AAEAAQACowAABMCGADHAYAABAAEAAqMAAATAXQAEwE4AAQABAAKjAAAEwIYEAcByAAEAAQACowAABMEz0A3AKgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwDwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBgABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","answerfrom":"194.0.36.1"}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"194.0.36.1","data":"j+SAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMcAOwCoAAQABAAKjAAAEgjsfHcBgAAEAAQACowAABMCGADHATgABAAEAAqMAAATAXQAEwDwAAQABAAKjAAAEwIYEAcByAAEAAQACowAABMEz0A3AKgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwGAAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBOABwAAQACowAAECABBmAwBQABAAAAAAABAALAPAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","timestamp":1626875471.28184,"querytime":29}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"wQ6AAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAIAAQACowAABgNuczHAFsAWAAIAAQACowAACgNuczYDZXh0wBbAFgACAAEAAqMAAAYDbnMzwBbAFgACAAEAAqMAAAYDbnMxwETAFgACAAEAAqMAAAYDbnMywBbAQAABAAEAAqMAAASCOx8dwFYAAQABAAKjAAAEwIYAMcB6AAEAAQACowAABMBdAATALgABAAEAAqMAAATAhgQBwGgAAQABAAKjAAAEwTPQDcBAABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AVgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwHoAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsAuABwAAQACowAAECABBnwiGAACAAAAAAAEAAE=","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875474.42991}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"LNGAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zNsAOwHIAAQABAAKjAAAEgjsfHcA8AAEAAQACowAABMCGADHAYAABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYEAcBOAAEAAQACowAABMEz0A3AcgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwDwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBgABwAAQACowAAECABBmAwBQABAAAAAAABAALAKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875471.62653}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"XLyAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAGA25zM8AQwBAAAgABAAKjAAAKA25zNgNleHTAEMAQAAIAAQACowAABgNuczHATMAQAAIAAQACowAABgNuczLAEMAMAAEAAQACowAABMCGBAHASAABAAEAAqMAAASCOx8dwDYAAQABAAKjAAAEwIYAMcBwAAEAAQACowAABMBdAATAXgABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBIABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/ANgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwHAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAg==","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875446.91355}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Yw6AAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAAAEAAcAWAAIAAQACowAABgNuczLAFsAWAAIAAQACowAABgNuczHAFsAWAAIAAQACowAABgNuczPAFsAWAAIAAQACowAACgNuczEDZXh0wBbAFgACAAEAAqMAAAYDbnM2wGjAegABAAEAAqMAAASCOx8dwFIAAQABAAKjAAAEwIYAMcAuAAEAAQACowAABMBdAATAQAABAAEAAqMAAATAhgQBwGQAAQABAAKjAAAEwTPQDcB6ABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/AUgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwC4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBAABwAAQACowAAECABBnwiGAACAAAAAAAEAAE=","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875474.2619}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"KRKAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAGA25zM8AQwBAAAgABAAKjAAAGA25zMcAQwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczbAXsAMAAEAAQACowAABMBdAATAcAABAAEAAqMAAASCOx8dwDYAAQABAAKjAAAEwIYAMcBIAAEAAQACowAABMCGBAHAWgABAAEAAqMAAATBM9ANwAwAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBwABwAAQACowAAECABBiAAAAD/AAAAAAAAAC/ANgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwEgAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAQ==","answerfrom":"194.0.36.1","querytime":29,"timestamp":1626875447.41958}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":29,"timestamp":1626875467.88597,"data":"p++AAAABAAAABQAJAWQDbmljAmZyAAAcAAHADgACAAEAAqMAAAYDbnMxwA7ADgACAAEAAqMAAAoDbnMxA2V4dMAOwA4AAgABAAKjAAAGA25zNsA8wA4AAgABAAKjAAAGA25zM8AOwA4AAgABAAKjAAAGA25zMsAOwE4AAQABAAKjAAAEgjsfHcBgAAEAAQACowAABMCGADHAcgABAAEAAqMAAATAXQAEwCYAAQABAAKjAAAEwIYEAcA4AAEAAQACowAABMEz0A3ATgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwGAAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcByABwAAQACowAAECABBmAwBQABAAAAAAABAALAJgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAAB","answerfrom":"194.0.36.1"}}}} +g.ext.nic.fr 2001:0678:004c:0000:0000:0000:0000:0001 {"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.55524,"querytime":30,"answerfrom":"2001:678:4c::1","data":"SyWAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAGA25zMcAQwBAAAgABAAKjAAAKA25zNgNleHTAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczHATMAMAAEAAQACowAABMBdAATADAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwEgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BeABwAAQACowAAECABBmAwBgABAAAAAAABAAHANgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwEgAAQABAAKjAAAEgjsfHcBeAAEAAQACowAABMCGADHANgABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEwTPQDQ=="}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":30,"timestamp":1626875437.47859,"data":"yraAAAABAAAABQAJAWQDbmljAmZyAAAcAAHADgACAAEAAqMAAAoDbnM2A2V4dMAOwA4AAgABAAKjAAAGA25zMcAqwA4AAgABAAKjAAAGA25zMcAOwA4AAgABAAKjAAAGA25zMsAOwA4AAgABAAKjAAAGA25zM8AOwCYAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8ByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAYAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwE4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAmAAEAAQACowAABII7Hx3AcgABAAEAAqMAAATAhgAxwGAAAQABAAKjAAAEwF0ABMBOAAEAAQACowAABMCGBAHAPAABAAEAAqMAAATBM9AN","answerfrom":"2001:678:4c::1"}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"av+AAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAAAEAAcAWAAIAAQACowAABgNuczLAFsAWAAIAAQACowAACgNuczYDZXh0wBbAFgACAAEAAqMAAAYDbnMxwBbAFgACAAEAAqMAAAYDbnMxwETAFgACAAEAAqMAAAYDbnMzwBbAQAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwHoAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAuABwAAQACowAAECABBmAwBQABAAAAAAABAALAVgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwEAAAQABAAKjAAAEgjsfHcB6AAEAAQACowAABMCGADHALgABAAEAAqMAAATAXQAEwFYAAQABAAKjAAAEwIYEAcBoAAEAAQACowAABMEz0A0=","answerfrom":"2001:678:4c::1","querytime":29,"timestamp":1626875456.02153}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9yOAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczHALMAMAAEAAQACowAABMCGBAHADAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwCgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BMABwAAQACowAAECABBmAwBgABAAAAAAABAAHAXgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwCgAAQABAAKjAAAEgjsfHcBMAAEAAQACowAABMCGADHAXgABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:678:4c::1","querytime":29,"timestamp":1626875439.24637}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":30,"timestamp":1626875438.49424,"data":"OZGAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcAOwGAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8AqABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwE4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBgAAEAAQACowAABII7Hx3AKgABAAEAAqMAAATAhgAxwDwAAQABAAKjAAAEwF0ABMBOAAEAAQACowAABMCGBAHAcgABAAEAAqMAAATBM9AN","answerfrom":"2001:678:4c::1"}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"m8qAAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAIAAQACowAABgNuczLAFsAWAAIAAQACowAACgNuczYDZXh0wBbAFgACAAEAAqMAAAYDbnMxwETAFgACAAEAAqMAAAYDbnMxwBbAFgACAAEAAqMAAAYDbnMzwBbAQAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwHoAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAuABwAAQACowAAECABBmAwBQABAAAAAAABAALAaAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwEAAAQABAAKjAAAEgjsfHcB6AAEAAQACowAABMCGADHALgABAAEAAqMAAATAXQAEwGgAAQABAAKjAAAEwIYEAcBWAAEAAQACowAABMEz0A0=","timestamp":1626875456.17463,"querytime":29}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875438.18779,"querytime":29,"answerfrom":"2001:678:4c::1","data":"3HiAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zM8ASwCoAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8ByABwAAQACowAAECABBmAwBgABAAAAAAABAAHATgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwGAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAqAAEAAQACowAABII7Hx3AcgABAAEAAqMAAATAhgAxwE4AAQABAAKjAAAEwF0ABMBgAAEAAQACowAABMCGBAHAPAABAAEAAqMAAATBM9AN"}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"X3WAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zM8ASwDwAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8ByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwE4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcA8AAEAAQACowAABII7Hx3AcgABAAEAAqMAAATAhgAxwCoAAQABAAKjAAAEwF0ABMBOAAEAAQACowAABMCGBAHAYAABAAEAAqMAAATBM9AN","answerfrom":"2001:678:4c::1","querytime":29,"timestamp":1626875437.73176}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875440.02382,"querytime":29,"answerfrom":"2001:678:4c::1","data":"QpiAAAABAAAABQAJA25zMwNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczHALMAMABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwF4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAhgAxwCgAAQABAAKjAAAEgjsfHcBeAAEAAQACowAABMBdAATATAABAAEAAqMAAATAhgQBwHAAAQABAAKjAAAEwTPQDQ=="}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"JEqAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwDwAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8AqABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwGAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcA8AAEAAQACowAABII7Hx3AKgABAAEAAqMAAATAhgAxwHIAAQABAAKjAAAEwF0ABMBgAAEAAQACowAABMCGBAHATgABAAEAAqMAAATBM9AN","timestamp":1626875438.34475,"querytime":30}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"b+uAAAABAAAAAwAGBWFmbmljAmZyAAAGAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zM8AqwAwAAgABAAKjAAAGA25zMcAqwDwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAmABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwDwAAQABAAKjAAAEwIYAMcAmAAEAAQACowAABMBdAATATgABAAEAAqMAAATAhgQB","timestamp":1626875437.01824,"querytime":29}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":30,"timestamp":1626875439.70984,"data":"692AAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAYDbnM2wCzAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAALADMAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAUAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwGIAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcA+ABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAXQAEwFAAAQABAAKjAAAEgjsfHcBiAAEAAQACowAABMCGADHAPgABAAEAAqMAAATAhgQBwCgAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:678:4c::1"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"EZGAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAUMAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHATAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAvwDoAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAoABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAhgQBwEwAAQABAAKjAAAEgjsfHcA6AAEAAQACowAABMCGADHAKAABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEwTPQDQ==","timestamp":1626875439.39159,"querytime":30}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"4hWAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zNsAOwHIAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8A8ABwAAQACowAAECABBmAwBgABAAAAAAABAAHATgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwCoAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcByAAEAAQACowAABII7Hx3APAABAAEAAqMAAATAhgAxwE4AAQABAAKjAAAEwF0ABMAqAAEAAQACowAABMCGBAHAYAABAAEAAqMAAATBM9AN","timestamp":1626875438.04219,"querytime":30}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"lWKAAAABAAAABQAJAWQDbmljAmZyAAABAAHADgACAAEAAqMAAAoDbnM2A2V4dMAOwA4AAgABAAKjAAAGA25zMcAOwA4AAgABAAKjAAAGA25zMcAqwA4AAgABAAKjAAAGA25zM8AOwA4AAgABAAKjAAAGA25zMsAOwCYAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwDwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAmAAEAAQACowAABII7Hx3AYAABAAEAAqMAAATAhgAxwHIAAQABAAKjAAAEwF0ABMA8AAEAAQACowAABMCGBAHATgABAAEAAqMAAATBM9AN","answerfrom":"2001:678:4c::1","querytime":29,"timestamp":1626875437.32031}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"mk2EAAABAAEABAAAAmZyAAAGAAHADAAGAAEAAqMAADAIbnNtYXN0ZXIDbmljwAwKaG9zdG1hc3RlcsAphMa+GwAADhAAAAcIADbugAAAFRjADAACAAEAAqMAAAQBZMApwAwAAgABAAKjAAAIAWYDZXh0wCnADAACAAEAAqMAAAQBZcBuwAwAAgABAAKjAAAEAWfAbg==","timestamp":1626875437.1758,"querytime":29}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":29,"timestamp":1626875439.8744,"data":"aXCAAAABAAAABQAJA25zMwNuaWMCZnIAAAEAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHALMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczLAEMAMAAEAAQACowAABMCGADHADAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwCgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BwABwAAQACowAAECABBmAwBQABAAAAAAABAALAXgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwCgAAQABAAKjAAAEgjsfHcBwAAEAAQACowAABMBdAATAXgABAAEAAqMAAATAhgQBwEwAAQABAAKjAAAEwTPQDQ==","answerfrom":"2001:678:4c::1"}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:678:4c::1","data":"v6CAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMsASwE4AHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAAL8BgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwCoAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBOAAEAAQACowAABII7Hx3AYAABAAEAAqMAAATAhgAxwHIAAQABAAKjAAAEwF0ABMAqAAEAAQACowAABMCGBAHAPAABAAEAAqMAAATBM9AN","timestamp":1626875437.88338,"querytime":30}}}} +rirns.arin.net 199.253.249.53 {"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":106,"timestamp":1626875443.17827,"data":"TYWAAAABAAAABAAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDwAAgABAAKjAAAQA25zMQdyZW5hdGVyAmZyAMA8AAIAAQACowAABgNuczLAasA8AAIAAQACowAADARpbWFnBGltYWfAcsA8AAIAAQACowAACgNuczMDbmljwHI=","answerfrom":"199.253.249.53"}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"D6eAAAABAAAABAAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDwAAgABAAKjAAAOBGltYWcEaW1hZwJmcgDAPAACAAEAAqMAAA4DbnMyB3JlbmF0ZXLAcMA8AAIAAQACowAABgNuczHAhMA8AAIAAQACowAACgNuczMDbmljwHA=","answerfrom":"199.253.249.53","querytime":104,"timestamp":1626875442.46085}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"DEaAAAABAAAAAwAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwDQAAgABAAKjAAAMA25zMgNuaWMCZnIAwDQAAgABAAKjAAAGA25zM8BqwDQAAgABAAKjAAAGA25zMcBq","answerfrom":"199.253.249.53","querytime":104,"timestamp":1626875441.13162}}}} +f.gtld-servers.net 192.35.51.30 {} ns3.arin.net 2001:0500:00a9:0000:0000:0000:0000:0108 {} -ns2.arin.net 2001:0500:0031:0000:0000:0000:0000:0108 {} -ns2.arin.net 199.71.0.108 {} -c.ns.se 192.36.135.107 {} -c.ns.se 2001:067c:2554:0301:0000:0000:0000:0053 {} -c.gtld-servers.net 192.26.92.30 {} -g.ext.nic.fr 2001:0678:004c:0000:0000:0000:0000:0001 {"kPMzuIR4sho5B5qTORsFWg":null,"SwbApgSh9mA6B3tXP/GkUg":null,"dqdRTp15sptDjWNc+Ny8fA":null,"0Y1OnyHHHeY64pWuEem8Fg":null,"f39y8o9yMy+2CJ3Tild/Hw":null,"8QJIMKoYjfeWdcOUu1cfeg":null,"QOkoGas2H66m/7tFl3RUhA":null,"v3bnT3mxa7enSqJowgjjtA":null,"adP+90hFKW1Uzd20+9yl5g":null,"e4zVImtII38cTuTEUukPnA":null,"/ad1wr3S5M12N8m7PKZMlg":null,"uLlSKdcUBsdWu2uNVqHtoA":null,"PBlOUdigoXz0ayiyPtsxqw":null,"4YeoSYuQtm/OKn1ycfuP2Q":null,"29HegVtbyPWaDm48Y6S6RQ":null,"RE9uT98EtnHNKxECV54OHw":null,"SPBaDls0FbzNIjP4Lr4aZg":null,"QIgmZF9uZaI1+8Y34m3zGw":null,"EFDojD6JfBcmEaLwh9qkiA":null,"td2cmo+X37Q32z0SfAhp8Q":null,"Lm8PThfvr4TSG5Oo/xY+Jw":null,"BoP52jf+VIVO3VrSWkuPQQ":null} -g.ext.nic.fr 194.0.36.1 {"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"g3GAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zNsAOwE4AAQABAAKjAAAEwTPQDcAqAAEAAQACowAABMCGBAHAPAABAAEAAqMAAATAXQAEwGAAAQABAAKjAAAEwIYAMcByAAEAAQACowAABII7Hx3AKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwDwAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","answerfrom":"194.0.36.1","querytime":10,"timestamp":1552922477.20296}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922478.68312,"answerfrom":"194.0.36.1","data":"rbSAAAABAAAABQAJA25zMgNuaWMCZnIAAAEAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zNgNleHTAEMAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczHAOsAMAAEAAQACowAABMBdAATAcAABAAEAAqMAAATBM9ANwF4AAQABAAKjAAAEwIYEAcBMAAEAAQACowAABMCGADHANgABAAEAAqMAAASCOx8dwAwAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBeABwAAQACowAAECABBnwiGAACAAAAAAAEAAHATAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwDYAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922478.45232,"answerfrom":"194.0.36.1","data":"MCOAAAABAAAABQAJA25zMQNuaWMCZnIAAAEAAcAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAPsAQAAIAAQACowAABgNuczPAEMAMAAEAAQACowAABMCGBAHAXgABAAEAAqMAAATBM9ANwCgAAQABAAKjAAAEwF0ABMBwAAEAAQACowAABMCGADHAOgABAAEAAqMAAASCOx8dwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAoABwAAQACowAAECABBmAwBQABAAAAAAABAALAcAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwDoAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"KxqAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zM8ASwDwAAQABAAKjAAAEwTPQDcBOAAEAAQACowAABMCGBAHAYAABAAEAAqMAAATAXQAEwHIAAQABAAKjAAAEwIYAMcAqAAEAAQACowAABII7Hx3ATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","querytime":10,"timestamp":1552922477.56322,"answerfrom":"194.0.36.1"}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AtuAAAABAAAABQAJAWcDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zM8ASwE4AAQABAAKjAAAEwTPQDcAqAAEAAQACowAABMCGBAHAYAABAAEAAqMAAATAXQAEwHIAAQABAAKjAAAEwIYAMcA8AAEAAQACowAABII7Hx3AKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","timestamp":1552922477.6841,"querytime":10,"answerfrom":"194.0.36.1"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"GZyAAAABAAAABQAJA25zMgNuaWMCZnIAABwAAcAQAAIAAQACowAABgNuczPAEMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczbAPsAMABwAAQACowAAECABBmAwBQABAAAAAAABAALADAABAAEAAqMAAATAXQAEwDoAAQABAAKjAAAEwTPQDcBeAAEAAQACowAABMCGBAHAKAABAAEAAqMAAATAhgAxwHAAAQABAAKjAAAEgjsfHcBeABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAKAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","querytime":10,"answerfrom":"194.0.36.1","timestamp":1552922478.79396}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"tiCAAAABAAAABQAJAWQDbmljAmZyAAABAAHADgACAAEAAqMAAAYDbnMxwA7ADgACAAEAAqMAAAYDbnMywA7ADgACAAEAAqMAAAoDbnMxA2V4dMAOwA4AAgABAAKjAAAGA25zM8AOwA4AAgABAAKjAAAGA25zNsBOwEoAAQABAAKjAAAEwTPQDcAmAAEAAQACowAABMCGBAHAOAABAAEAAqMAAATAXQAEwGAAAQABAAKjAAAEwIYAMcByAAEAAQACowAABII7Hx3AJgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwDgAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAcgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","answerfrom":"194.0.36.1","querytime":10,"timestamp":1552922476.85313}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"y7GAAAABAAAABQAJA25zMwNuaWMCZnIAAAEAAcAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMxwD7AEAACAAEAAqMAAALADMAQAAIAAQACowAABgNuczLAEMAMAAEAAQACowAABMCGADHAUAABAAEAAqMAAATBM9ANwCgAAQABAAKjAAAEwIYEAcBwAAEAAQACowAABMBdAATAOgABAAEAAqMAAASCOx8dwAwAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcAoABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAcAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwDoAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1552922478.90884,"querytime":10,"answerfrom":"194.0.36.1"}}},"QOkoGas2H66m/7tFl3RUhA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":9,"answerfrom":"194.0.36.1","timestamp":1552922480.57106,"data":"s3uAAAABAAAAAgACBnJlc29uZQx1bml2LXJlbm5lczECZnIAAAEAAcATAAIAAQACowAAAsAMwBMAAgABAAKjAAAOBnNvbGVpbAR1dnNxwCDADAABAAEAAqMAAASBFP4BwEIAAQABAAKjAAAEwTMYAQ=="}}},"e4zVImtII38cTuTEUukPnA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"qMuAAAABAAAABQAJAWQDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zMcAOwHIAAQABAAKjAAAEwTPQDcAqAAEAAQACowAABMCGBAHATgABAAEAAqMAAATAXQAEwGAAAQABAAKjAAAEwIYAMcA8AAEAAQACowAABII7Hx3AKgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwE4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","timestamp":1552922476.53915,"querytime":10,"answerfrom":"194.0.36.1"}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"OmmAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zM8ASwGAAAQABAAKjAAAEwTPQDcA8AAEAAQACowAABMCGBAHATgABAAEAAqMAAATAXQAEwHIAAQABAAKjAAAEwIYAMcAqAAEAAQACowAABII7Hx3APAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwE4AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAKgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv","timestamp":1552922477.31676,"querytime":10,"answerfrom":"194.0.36.1"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922479.02932,"querytime":10,"answerfrom":"194.0.36.1","data":"XWaAAAABAAAABQAJA25zMwNuaWMCZnIAABwAAcAQAAIAAQACowAAAsAMwBAAAgABAAKjAAAKA25zMQNleHTAEMAQAAIAAQACowAABgNuczHAEMAQAAIAAQACowAABgNuczLAEMAQAAIAAQACowAABgNuczbAOsAMABwAAQACowAAECABBmAwBgABAAAAAAABAAHADAABAAEAAqMAAATAhgAxwDYAAQABAAKjAAAEwTPQDcBMAAEAAQACowAABMCGBAHAXgABAAEAAqMAAATAXQAEwHAAAQABAAKjAAAEgjsfHcBMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAXgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"uLlSKdcUBsdWu2uNVqHtoA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Pu6AAAABAAAAAgACBnJlc29uZQx1bml2LXJlbm5lczECZnIAABwAAcATAAIAAQACowAADgZzb2xlaWwEdXZzccAgwBMAAgABAAKjAAACwAzADAABAAEAAqMAAASBFP4BwDQAAQABAAKjAAAEwTMYAQ==","answerfrom":"194.0.36.1","querytime":10,"timestamp":1552922480.64908}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"answerfrom":"194.0.36.1","timestamp":1552922476.31967,"data":"45mAAAABAAAAAwAGBWFmbmljAmZyAAAGAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zM8AqwAwAAgABAAKjAAAGA25zMsAqwCYAAQABAAKjAAAEwIYEAcBOAAEAAQACowAABMBdAATAPAABAAEAAqMAAATAhgAxwCYAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBOABwAAQACowAAECABBmAwBQABAAAAAAABAALAPAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922477.43416,"answerfrom":"194.0.36.1","data":"MqmAAAABAAAABQAJAWYDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zMcAOwHIAAQABAAKjAAAEwTPQDcBOAAEAAQACowAABMCGBAHAYAABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYAMcA8AAEAAQACowAABII7Hx3ATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGAAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsAqABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv"}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"194.0.36.1","querytime":10,"timestamp":1552922488.18364,"data":"ZM2AAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAAAEAAcAWAAIAAQACowAABgNuczHAFsAWAAIAAQACowAABgNuczLAFsAWAAIAAQACowAACgNuczEDZXh0wBbAFgACAAEAAqMAAAYDbnMzwBbAFgACAAEAAqMAAAYDbnM2wFbAUgABAAEAAqMAAATBM9ANwC4AAQABAAKjAAAEwIYEAcBAAAEAAQACowAABMBdAATAaAABAAEAAqMAAATAhgAxwHoAAQABAAKjAAAEgjsfHcAuABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAQAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwGgAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcB6ABwAAQACowAAECABBiAAAAD/AAAAAAAAAC8="}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"OBuEAAABAAEABQAAAmZyAAAGAAHADAAGAAEAAqMAADAIbnNtYXN0ZXIDbmljwAwKaG9zdG1hc3RlcsAphKFAoAAADhAAAAcIADbugAAAFRjADAACAAEAAqMAAAgBZwNleHTAKcAMAAIAAQACowAABAFlwF7ADAACAAEAAqMAAAQBZMBewAwAAgABAAKjAAAEAWTAKcAMAAIAAQACowAABAFmwF4=","timestamp":1552922476.43196,"querytime":10,"answerfrom":"194.0.36.1"}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922476.9711,"querytime":10,"answerfrom":"194.0.36.1","data":"R6mAAAABAAAABQAJAWQDbmljAmZyAAAcAAHADgACAAEAAqMAAAoDbnM2A2V4dMAOwA4AAgABAAKjAAAGA25zMcAOwA4AAgABAAKjAAAGA25zMcAqwA4AAgABAAKjAAAGA25zM8AOwA4AAgABAAKjAAAGA25zMsAOwE4AAQABAAKjAAAEwTPQDcA8AAEAAQACowAABMCGBAHAcgABAAEAAqMAAATAXQAEwGAAAQABAAKjAAAEwIYAMcAmAAEAAQACowAABII7Hx3APAAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwHIAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsBgABwAAQACowAAECABBmAwBgABAAAAAAABAAHAJgAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"VFGAAAABAAAABQAJA25zMQNuaWMCZnIAABwAAcAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAYDbnMxwCzAEAACAAEAAqMAAAYDbnMywBDAEAACAAEAAqMAAALADMAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHADAABAAEAAqMAAATAhgQBwFAAAQABAAKjAAAEwTPQDcBiAAEAAQACowAABMBdAATAPgABAAEAAqMAAATAhgAxwCgAAQABAAKjAAAEgjsfHcBiABwAAQACowAAECABBmAwBQABAAAAAAABAALAPgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwCgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","answerfrom":"194.0.36.1","querytime":10,"timestamp":1552922478.565}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"WJOAAAABAAAABQAJCWRuc21hc3RlcgNuaWMCZnIAABwAAcAWAAIAAQACowAABgNuczHAFsAWAAIAAQACowAACgNuczEDZXh0wBbAFgACAAEAAqMAAAYDbnMywBbAFgACAAEAAqMAAAYDbnM2wETAFgACAAEAAqMAAAYDbnMzwBbAQAABAAEAAqMAAATBM9ANwC4AAQABAAKjAAAEwIYEAcBWAAEAAQACowAABMBdAATAegABAAEAAqMAAATAhgAxwGgAAQABAAKjAAAEgjsfHcAuABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAVgAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHoAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcBoABwAAQACowAAECABBiAAAAD/AAAAAAAAAC8=","querytime":10,"answerfrom":"194.0.36.1","timestamp":1552922488.30172}}},"EFDojD6JfBcmEaLwh9qkiA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"answerfrom":"194.0.36.1","timestamp":1552922476.65707,"data":"RaGAAAABAAAABQAJAWQDZXh0A25pYwJmcgAAHAABwBIAAgABAAKjAAAGA25zM8ASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zMsASwGAAAQABAAKjAAAEwTPQDcBOAAEAAQACowAABMCGBAHAcgABAAEAAqMAAATAXQAEwCoAAQABAAKjAAAEwIYAMcA8AAEAAQACowAABII7Hx3ATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwHIAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsAqABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv"}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":10,"timestamp":1552922477.08624,"answerfrom":"194.0.36.1","data":"8GuAAAABAAAABQAJAWUDZXh0A25pYwJmcgAAAQABwBIAAgABAAKjAAAGA25zMsASwBIAAgABAAKjAAAGA25zNsAOwBIAAgABAAKjAAAGA25zMcASwBIAAgABAAKjAAAGA25zMcAOwBIAAgABAAKjAAAGA25zM8ASwGAAAQABAAKjAAAEwTPQDcBOAAEAAQACowAABMCGBAHAKgABAAEAAqMAAATAXQAEwHIAAQABAAKjAAAEwIYAMcA8AAEAAQACowAABII7Hx3ATgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwCoAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsByABwAAQACowAAECABBmAwBgABAAAAAAABAAHAPAAcAAEAAqMAABAgAQYgAAAA/wAAAAAAAAAv"}}}} -ns2.nic.fr 192.93.0.4 {"0TlRUFu34T/7a+Z9BxkQVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"BEqEAAABAAEAAQAAA1dXdwVBRm5pYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":18,"timestamp":1552922501.38661,"answerfrom":"192.93.0.4"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922487.89336}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"timestamp":1552922487.24202,"answerfrom":"192.93.0.4","data":"4J+EAAABAAEAAwAGBWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgAMAAAAAAAAAAlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczLARsAMAAIAAQACowAABgNuczPARsBCAAEAAQACowAABMCGBAHAWAABAAEAAqMAAATAXQAEwGoAAQABAAKjAAAEwIYAMcBCABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAWAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwGoAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAQ=="}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"tcaEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwGMAAQABAAKjAAAEwIYEAcCHAAEAAQACowAABMBdAATAdQABAAEAAqMAAATAhgAxwGMAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcCHABwAAQACowAAECABBmAwBQABAAAAAAABAALAdQAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB","answerfrom":"192.93.0.4","querytime":36,"timestamp":1552922481.93612}}},"MgH4T3MlBDXU0qaxaRcnAA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"timestamp":1552922501.48924,"answerfrom":"192.93.0.4","data":"aMiEAAABAAEAAQAAA3dXVwVhRk5pYwJmUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"Y/FppjiF5p7LpFx+5iKNTQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922487.44172,"data":"LtyEAAABAAEAAQAAA3d3VwVhRm5JYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"WiGEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAQgBAAMIAwEAAdPrdwx/CIhOZf01Mv8PA3X3i2VzrrbC8fPXWpSMy6ki0QvyLCq6ZUf4lcPCd0VucqTztRpyrQqhYSfDBUC2tTgAbfNtL/icoWKO0QNyPlilWlsNE5mvQ0cqORRNX0V2AZTReComqVivpe81tgJFL8HMFxqRtcIA1ngo2i4atZ5STm05dBXZzlPxpFvwefHRZW3VfF3wudskg8PHCLvfOhuSie/tWEd2iBfba80i8pe96iZGJZ5YZFTf9kZeWcCC5E6pZtDstlUTyII0DZxo2OZ42YBUR4pzAcCLU6wGodZLGJcPXOb/wS1BmzNI3r+OaKr8N/0YD8xJuQEmJ/dN64fADAAwAAEAAqMAAQgBAQMIAwEAAdPH3dD6UIEkcVMThgn7fUgbxw8GJfXbWXrpCc7J8hR5EsNdZ7whc6oqa3ziZetJecJ318KqIT75I/yDwp3FCk6936tTdnntmDwl5CNn0F16OOtP4kZOUDs2hwWEcyz5sUPPs5v5Vuoo6/piPBXh6pS+J5AAqzLIAK11sXU67ORDVdHXIRV8VDGne3p+jV/PmSzgdAZfJ5xUxMuV76DrlWZkKIn/658Zl+Zat0y85FrsmXPYMG/8Y/l0J0jtqgaTUIRVM2ZryoKDH98UH8tmKfXK87eqKLV4RHvd/wXBgv65d1+JOrf2R3J1LQNhsyAdlED2e111VLKDgxcjc66HOPfADAAwAAEAAqMAAQgBAAMIAwEAAaqBT2ZQrM4q3xjx++/rY6vXzxam2pImOAYONiOM9xfyPH2XmBPU9ghseSe/idBMyjoLP7S017wFZymo+45PAWTSPsPUJrf2s8kOXVsuSFBr6sM2dQM3dnDRcXdGcLUrJksXrI7QGVSOFWKOcYPe1xzEbf2VP68I2sWaaMp7QMkDADew7nFxV/dP1RCMhMJ6y575TvOg7z/L1WV1d0fyxZ4miftL6z/5+u2KWDdXISUSSa/HcRSQAWtbHgWds+34lQMmXlrDWI3sq/kkWr+KDJ/M01+3cX94rGGEPAp1x4ZJiwdzLsvBTC9z8cgVpGgeAF/oxN55nL3EAvFs8AhomkPADAAuAAEAAqMAARwAMAgCAAKjAFyxUIBciZEWkuoFYWZuaWMCZnIAT0EnEMVHTIAnBuuSwkCZDNXAkpMDT0E60KXi5iaiqcWyXXOW9b/yXEBFKlpG72kp9v2qtBzpPsZHafk+7A4B6BomBEoM8SuDCqt6Tdz/apNXp3mc3sewL9jF56ALPGolozmIEFZmstBUn4NcBxntRTzxH+GyH5UB9Jn4TJVq+wGVUx6QYdg9L8uAjkcP3jBQAb8L1czmemiF2r1MMbTCPYRebdK5gQFhdgREOXwj2mc8RjcPxuud/CM01vxnQwYobCKt+deeropJi//JN13RPGJ2siexMxe8IOlmhq9Qfzoik2OwwPtfQTIVHdRcw0KbiDLAGGoNLKCmBcsDQxnbeQAAKQWYAACAAAAA","answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922486.44453}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922486.77697,"querytime":18,"answerfrom":"192.93.0.4","data":"OMiABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ=="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1552922487.81123,"querytime":18,"answerfrom":"192.93.0.4"}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"nK6ABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922486.75213}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":18,"answerfrom":"192.93.0.4","timestamp":1552922487.34879,"data":"68GABQABAAAAAAAAAAACAAE="}}},"KsaIpCVhpeMsuVQal+DZhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"5f2EAAABAAEAAQAAA3dXVwVhZk5pQwJGUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":18,"timestamp":1552922487.54029,"answerfrom":"192.93.0.4"}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","querytime":18,"timestamp":1552922487.72233,"answerfrom":"192.93.0.4"}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922486.80288,"querytime":18,"answerfrom":"192.93.0.4","data":"vnqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB"}}},"deQZZWplsy7gFo3gqzBFcg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"fZ6EAAABAAEAAQAAA1dXVwVhZk5JQwJmcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922487.56681}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922486.11559,"data":"2S6EAAABAAMAAAAGBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zM8AqwCYAAQABAAKjAAAEwIYEAcA8AAEAAQACowAABMBdAATATgABAAEAAqMAAATAhgAxwCYAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcA8ABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"P5BT4xLKPc0cvgR8IR22ww":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"6lyEAAABAAEAAQAAA1dXdwVhZm5pQwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":18,"answerfrom":"192.93.0.4","timestamp":1552922501.46298}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"g+KEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","querytime":18,"timestamp":1552922487.01152,"answerfrom":"192.93.0.4"}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.93.0.4","querytime":18,"timestamp":1552922481.86545,"data":"+rmEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zMsAwwHUAAQABAAKjAAAEwIYEAcCHAAEAAQACowAABMBdAATAYwABAAEAAqMAAATAhgAxwHUAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcCHABwAAQACowAAECABBmAwBQABAAAAAAABAALAYwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}}} -ns2.nic.fr 2001:0660:3005:0001:0000:0000:0001:0002 {"deQZZWplsy7gFo3gqzBFcg":null,"auoc0YjagPcFkyMHmxRk1g":null,"zueeZkdHfWptXkiyz6gmOA":null,"KsaIpCVhpeMsuVQal+DZhQ":null,"UzfnoYKACE71u6V/QQ17nw":null,"4YeoSYuQtm/OKn1ycfuP2Q":null,"pOu0bQGV1Z/qItEzagW7FA":null,"TJ/AJOD5oSy8le0E7lFXTg":null,"G5mY7b4q1l50Mf6YfNGUtg":null,"2/fg0ozhiGSsmWtVHzHJow":null,"nys7lrpg8L2EaHBxy2MfKg":null,"F8IuiH+hJkzDv9m0t6ioyA":null,"cgyeMZwmsWADV4zvkMbarg":null,"u2IVdHOj+hP1WgRzMMG0Hg":null,"CFtsJvzdNZr5xrBiWkborQ":null,"Y/FppjiF5p7LpFx+5iKNTQ":null} -ns2.nic.fr 192.134.4.1 {"Y/FppjiF5p7LpFx+5iKNTQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"1QOEAAABAAEAAQAAA3d3VwVhRm5JYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA==","querytime":1,"timestamp":1552922487.42673,"answerfrom":"192.134.4.1"}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7rqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922486.95384}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9ZKEAAABAAEABQAIA25zMQNuaWMCZnIAABwAAcAMABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAEAACAAEAAqMAAAYDbnMywBDAEAACAAEAAqMAAALADMAQAAIAAQACowAACgNuczEDZXh0wBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAYDbnM2wGjAZAABAAEAAqMAAATBM9ANwAwAAQABAAKjAAAEwIYEAcBEAAEAAQACowAABMBdAATAegABAAEAAqMAAATAhgAxwIwAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBmAwBQABAAAAAAABAALAegAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwIwAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.66774}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"MayEAAABAAIABAAIBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAAEcAAYIAgACowBctpmtXI7zNpV5BWFmbmljAmZyAF6LDoTpUAz47+zOXKAlvTGFubGj0aY/kCo/hWXID+P/HyaIpHu2wJ1AS9JS+8szFUc1xgXDYM9zWD74UWs7zQ2KDMZEo6cl4BrvmdCpiv78Ibbfq3eSuhrlo2eZs/IaogFx4CD2jWqODJyk1butmuXwXJognXi6slKUWf9Nv0u7wAktqr86wFgrF37LtjHrXWo6hVvRQpu/wRVkn4u5QFTuHaNowfzf4vBOgrLCNnMStJbkpNaxc+ZQ9Nz0JmtzvfiwmwrWLamRtybKXl6wZTSaiAR7h2P1LdM7SC45c1GNOj3dBbUZos4/oVvgE8zd3B3a9OzT1e2uuMh9UB7R8yzADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAARwAAggCAAKjAFyzsWFci6d2lXkFYWZuaWMCZnIAJ1sp9VayWIT0DZ5sxDsENjHeQjPB7dwDb2cK+uOhZiGuQb7QFdY3Odjp5tuBwlpGNy28Kh05ReK0SCXzGuXX7jF4V5xqhBVtla8fXkSrF1t/rAMfS+uXGj41+jfFeDkryYbzMen0TVHrEuTLN9FjNceJUIoka6HocMdp83ZOb6NDtO5e5ulC8KPUSsqVOXDRCjqKnKDvEFXQ5Hpl8zvbgcl7gGWZsD/GOChWvjiQr5zvBzofZtTne6QC7ryt28ud7iiFweQVbpkLb2Tm2Cc5D7HbDwS5H9J7/FKfYOGOQrJr9JTECyZ4het2DEq3OUIcTqrhvQYGCBNwvRPCwgqQJ8GvAAEAAQACowAABMCGBAHBnQABAAEAAqMAAATAXQAEwYsAAQABAAKjAAAEwIYAMcGvABwAAQACowAAECABBnwiGAACAAAAAAAEAAHBnQAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwYsAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAcGvAC4AAQACowABGgABCAMAAqMAXLJSp1yKs/+VeQNuaWMCZnIAJDUwzKZjE5jgX0eJlvWuzoKfX+cZ9MoYMWmzEwyWHPyRaX105nFG64W7ERDmTcFNx7V5L2lcDRRrcIwKPEYfWQDdkjvndKajZoELm7oJzcdzEe1JVeA6rc+kzxgCpSWvDc5sM/eYcfjkJ7B5XeZUr0VdunJDPD0pUGaLAAHMbt8aa9ly3HymaxC+PA8uJaFyinyeiGGKnyrYDgx7Kaf+RuSNFEuvEaAP9Xw93LpReYafGAGU+4vB4WXUPtzcBjUip+GYNQuo+Ekyg5eIgjreAsWbxSvvlhX0umsQZYofbXY99jHLTf6ND3LRMbe7n4cju02I4Wf0Wi/72TO+x4DN9gAAKQWYAACAAAAA","answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.40763}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922487.2317,"data":"3ZGEAAABAAEAAwAGBWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgAMAAAAAAAAAAlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczLARsBCAAEAAQACowAABMCGBAHAagABAAEAAqMAAATAXQAEwFgAAQABAAKjAAAEwIYAMcBCABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAagAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwFgAHAABAAKjAAAQIAEGYDAGAAEAAAAAAAEAAQ=="}}},"P5BT4xLKPc0cvgR8IR22ww":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922501.4485,"data":"6JeEAAABAAEAAQAAA1dXdwVhZm5pQwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"amIcOxD+RSRHr9RBm4BZsw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"4RiEAAABAAIAAwAMBWFmbmljAmZyAAAPAAHADAAPAAEAAqMAAAwACgNteDUDbmljwBLADAAPAAEAAqMAAAgAFANteDTALMAMAAIAAQACowAABgNuczHALMAMAAIAAQACowAABgNuczLALMAMAAIAAQACowAABgNuczPALMAoAAEAAQACowAABMCGBA3AQAABAAEAAqMAAATAhgQMwFIAAQABAAKjAAAEwIYEAcBkAAEAAQACowAABMBdAATAdgABAAEAAqMAAATAhgAxwCgAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAE8BAABwAAQACowAAECABBnwiGAACAAAAAAAEABLAUgAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwGQAHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsB2ABwAAQACowAAECABBmAwBgABAAAAAAABAAEDXzI1BF90Y3DAKAA0AAEAAqMAACMDAAGM7Kc+DWl7w5GB1/6VM8dXADe3vPfo5Nt8iabSexKN7gNfMjUEX3RjcMBAADQAAQACowAAIwMAAYzspz4NaXvDkYHX/pUzx1cAN7e89+jk23yJptJ7Eo3u","querytime":2,"timestamp":1552922488.12278,"answerfrom":"192.134.4.1"}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"answerfrom":"192.134.4.1","timestamp":1552922487.00535,"data":"yB2EAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1552922477.79371,"answerfrom":"192.134.4.1","data":"pUiEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwIcAAQABAAKjAAAEwIYEAcB1AAEAAQACowAABMBdAATAYwABAAEAAqMAAATAhgAxwIcAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcB1ABwAAQACowAAECABBmAwBQABAAAAAAABAALAYwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922486.70734,"data":"OUOEAAABAAEABQAIA25zMwNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBgABAAAAAAABAAHAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnM2wFrAEAACAAEAAqMAAAYDbnMywBDAVgABAAEAAqMAAATBM9ANwEQAAQABAAKjAAAEwIYEAcCMAAEAAQACowAABMBdAATADAABAAEAAqMAAATAhgAxwHoAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAjAAcAAEAAqMAABAgAQZgMAUAAQAAAAAAAQACwHoAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"deQZZWplsy7gFo3gqzBFcg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"answerfrom":"192.134.4.1","timestamp":1552922487.53232,"data":"uAeEAAABAAEAAQAAA1dXVwVhZk5JQwJmcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"KNmqNpIE1P0AaV9ISH94Iw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"p5GEAwABAAAACAABC3h4LS1leGFtcGxlBWFmbmljAmZyAAABAAHAGAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8AeCmhvc3RtYXN0ZXLAPHhX+vgAABwgAAAHCAAk6gAAABUYwBgALgABAAAVGAEcAAYIAgACowBctpmtXI7zNpV5BWFmbmljAmZyAF6LDoTpUAz47+zOXKAlvTGFubGj0aY/kCo/hWXID+P/HyaIpHu2wJ1AS9JS+8szFUc1xgXDYM9zWD74UWs7zQ2KDMZEo6cl4BrvmdCpiv78Ibbfq3eSuhrlo2eZs/IaogFx4CD2jWqODJyk1butmuXwXJognXi6slKUWf9Nv0u7wAktqr86wFgrF37LtjHrXWo6hVvRQpu/wRVkn4u5QFTuHaNowfzf4vBOgrLCNnMStJbkpNaxc+ZQ9Nz0JmtzvfiwmwrWLamRtybKXl6wZTSaiAR7h2P1LdM7SC45c1GNOj3dBbUZos4/oVvgE8zd3B3a9OzT1e2uuMh9UB7R8ywgdWM1Z2dxcWk0NDhnYXEzbjlmcnBkdjltMmtyaGxyNXDAGAAyAAEAABUYACsBAQABCI5sg30w/SoyFPahDka4VxdPQuZTh70jI+a5t2igAAdiAYAIAAKQwYsALgABAAAVGAEcADIIAwAAFRhcpycmXH8FFpV5BWFmbmljAmZyAEUnegDMeXEAtaEtFeRCSmuNwNLoQF9JBtB53323us4ySi+MTOCjYFQXmr8xyH82IYdgXd9xtLLLsyVvKByzh09rhSa1j+YgOHIC9kar8NJvCCE0HQvbBikexqe5+sqE54rBJKXQrVki7CyFExVGZ9JoQu9AKOVlzhNj2EcgLMKXgnYe+xbqUNIpwMElIHpmaHuwHoem1bGx4rCiO/gKUH79Mxo0K7s81LoHC+pwm+RR+YaiqBFK/ToVqM/BgLMnfPimrc+5i8BanV/UpG0mhWoh0Nu5HFX84Dtcg4pgqBjuVMMLTtA7IzpzH7/ikglYAA8zFbMKbnM3nCrFynJy/oAgdDhzZjJkZDIwYzYycDVtaDdjYWVwcmg4b2RlOW5kYmPAGAAyAAEAABUYACoBAQABCI5sg30w/SoyFPMLCGtSIREFaHdL95b9NhU3Guy5AAYEAAAAAALDCwAuAAEAABUYARwAMggDAAAVGFymo05cfpSWlXkFYWZuaWMCZnIAikUongjovNro7R2jmzEioQnV8EQViak2+xnatU2n1ae8v21Rbw71sCdEGQN3VJ4lKuigzG50co+GKzjkdNuHtoK1MxpXl4HaJ+N7tAMQHeQaBgMyZG7BctOJq2GFv8MTlnIaH2eixwosnAdSwoB80prlqB78Z0DoGWNVfAV7oyqt4jx9nyC3OqDpvUJzRtgcgnUeyvdL1hy85BYYtjd9X9UyE85UPSrU2m75wVDmoCHKY+lj9agaMJOwYhSsJpyjSfk5V+R3oBCf647xhC2/OFcvgd07J7T0eSnJhXldEyrB2eLfztW7OolS5zp+x7mub++Q7HD4Dq9vKfcA3c80KSA0azNrOWRuaDhwZzkza3ZicjRqZjBjMmptaGRlaDQ5aMAYADIAAQAAFRgAKgEBAAEIjmyDfTD9KjIUKCda7CFX5ufEgGGzr9rVkR/A7KUABgQAAAAAAsSKAC4AAQAAFRgBHAAyCAMAABUYXKYBdlx+6PaVeQVhZm5pYwJmcgCSuvEaqge7EIrZVpDtSjKlUNLlThD93QKTwuxSS+KCyQedCD+nHhlTWMw+vzeLrP6BsXQN4cJpEAWUE+m1zn44D4RTrBWADReb5WhzbEA0oiuVcd9g7UVSYT6zfRoFmk9kBp1bHtwrwXxKsTwK2ByYTD3qMUEMXI9uRhTlzv+7W3oXIrX2nBFDrSXrKs9SKCYzsIyZrMMbgOYjILvSeGGoRTISRbiN1HA94m/RhCTxpW4mhM40tUev8vA39T1wV/s6gfgNRRvZ5xHb6lVLozcQ1Hku7z28fQE6ZJbBQgcQJGKeGasnw1uUZD5Di4JrlAOcAy5EethoV7+kDH5D/xZzAAApBZgAAIAAAAA=","querytime":3,"answerfrom":"192.134.4.1","timestamp":1552922486.57306}}},"MgH4T3MlBDXU0qaxaRcnAA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1552922501.45319,"answerfrom":"192.134.4.1","data":"KKOEAAABAAEAAQAAA3dXVwVhRk5pYwJmUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"HEOABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922486.95872}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922486.69848,"querytime":2,"answerfrom":"192.134.4.1","data":"vQqEAAABAAEABQAIA25zMwNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGADHAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAAGA25zNsA8wBAAAgABAAKjAAAGA25zMcAQwBAAAgABAAKjAAAGA25zMsAQwBAAAgABAAKjAAACwAzAOAABAAEAAqMAAATBM9ANwGAAAQABAAKjAAAEwIYEAcByAAEAAQACowAABMBdAATATgABAAEAAqMAAASCOx8dwGAAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcByABwAAQACowAAECABBmAwBQABAAAAAAABAALADAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwE4AHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"GviEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAQgBAQMIAwEAAdPH3dD6UIEkcVMThgn7fUgbxw8GJfXbWXrpCc7J8hR5EsNdZ7whc6oqa3ziZetJecJ318KqIT75I/yDwp3FCk6936tTdnntmDwl5CNn0F16OOtP4kZOUDs2hwWEcyz5sUPPs5v5Vuoo6/piPBXh6pS+J5AAqzLIAK11sXU67ORDVdHXIRV8VDGne3p+jV/PmSzgdAZfJ5xUxMuV76DrlWZkKIn/658Zl+Zat0y85FrsmXPYMG/8Y/l0J0jtqgaTUIRVM2ZryoKDH98UH8tmKfXK87eqKLV4RHvd/wXBgv65d1+JOrf2R3J1LQNhsyAdlED2e111VLKDgxcjc66HOPfADAAwAAEAAqMAAQgBAAMIAwEAAaqBT2ZQrM4q3xjx++/rY6vXzxam2pImOAYONiOM9xfyPH2XmBPU9ghseSe/idBMyjoLP7S017wFZymo+45PAWTSPsPUJrf2s8kOXVsuSFBr6sM2dQM3dnDRcXdGcLUrJksXrI7QGVSOFWKOcYPe1xzEbf2VP68I2sWaaMp7QMkDADew7nFxV/dP1RCMhMJ6y575TvOg7z/L1WV1d0fyxZ4miftL6z/5+u2KWDdXISUSSa/HcRSQAWtbHgWds+34lQMmXlrDWI3sq/kkWr+KDJ/M01+3cX94rGGEPAp1x4ZJiwdzLsvBTC9z8cgVpGgeAF/oxN55nL3EAvFs8AhomkPADAAwAAEAAqMAAQgBAAMIAwEAAdPrdwx/CIhOZf01Mv8PA3X3i2VzrrbC8fPXWpSMy6ki0QvyLCq6ZUf4lcPCd0VucqTztRpyrQqhYSfDBUC2tTgAbfNtL/icoWKO0QNyPlilWlsNE5mvQ0cqORRNX0V2AZTReComqVivpe81tgJFL8HMFxqRtcIA1ngo2i4atZ5STm05dBXZzlPxpFvwefHRZW3VfF3wudskg8PHCLvfOhuSie/tWEd2iBfba80i8pe96iZGJZ5YZFTf9kZeWcCC5E6pZtDstlUTyII0DZxo2OZ42YBUR4pzAcCLU6wGodZLGJcPXOb/wS1BmzNI3r+OaKr8N/0YD8xJuQEmJ/dN64fADAAuAAEAAqMAARwAMAgCAAKjAFyxUIBciZEWkuoFYWZuaWMCZnIAT0EnEMVHTIAnBuuSwkCZDNXAkpMDT0E60KXi5iaiqcWyXXOW9b/yXEBFKlpG72kp9v2qtBzpPsZHafk+7A4B6BomBEoM8SuDCqt6Tdz/apNXp3mc3sewL9jF56ALPGolozmIEFZmstBUn4NcBxntRTzxH+GyH5UB9Jn4TJVq+wGVUx6QYdg9L8uAjkcP3jBQAb8L1czmemiF2r1MMbTCPYRebdK5gQFhdgREOXwj2mc8RjcPxuud/CM01vxnQwYobCKt+deeropJi//JN13RPGJ2siexMxe8IOlmhq9Qfzoik2OwwPtfQTIVHdRcw0KbiDLAGGoNLKCmBcsDQxnbeQAAKQWYAACAAAAA","answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.34768}}},"K1aB+vxTZc0RW5SYUgX9og":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.38618,"data":"t0iEAAABAAIABAAJBWFmbmljAmZyAAAzAAHADAAzAAEAAAAAAA0BAAABCI5sg30w/SoywAwALgABAAAAAAEcADMIAgAAAABcpv4zXH7M1pV5BWFmbmljAmZyAFfsUFsQQjK7+dJx4f01f8eJiyA3QTpObw/QXSBeJwC5eqsSgpQnLt0aUx5DiTBh5XVC8/p/Sd7+9LTVWr2xbtMksfH6/B/Z5gqnYb/PRCexnD0KAUK5/maIJALx7Uso6oNGWlzEzvE23MoqK4D7nWBRM7VdxFS6QZXCPRQL/5i76MTzZQgbunlmIpHvMluX0g099U3PPBz39D6hN8zElBEsLRnggVAs9XItdvrkzbkLb4juGqCX5PrvX4dgu5zCHfS7BnPm9u365dg9H3QNXbGmy6J+QGVz16nwZve5abAJJdIvIXEHX3HeuoQZu4ax5Ctmv88iDZkIgL2eUpnmuxbADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsFrwAwAAgABAAKjAAAGA25zM8FrwAwALgABAAKjAAEcAAIIAgACowBcs7FhXIundpV5BWFmbmljAmZyACdbKfVWsliE9A2ebMQ7BDYx3kIzwe3cA29nCvrjoWYhrkG+0BXWNznY6ebbgcJaRjctvCodOUXitEgl8xrl1+4xeFecaoQVbZWvH15Eqxdbf6wDH0vrlxo+Nfo3xXg5K8mG8zHp9E1R6xLkyzfRYzXHiVCKJGuh6HDHafN2Tm+jQ7TuXubpQvCj1ErKlTlw0Qo6ipyg7xBV0OR6ZfM724HJe4BlmbA/xjgoVr44kK+c7wc6H2bU53ukAu68rdvLne4ohcHkFW6ZC29k5tgnOQ+x2w8EuR/Se/xSn2DhjkKya/SUxAsmeIXrdgxKtzlCHE6q4b0GBggTcL0TwsIKkCfBZwABAAEAAqMAAATAhgQBwX0AAQABAAKjAAAEwF0ABMGPAAEAAQACowAABMCGADHBZwAcAAEAAqMAABAgAQZ8IhgAAgAAAAAABAABwX0AHAABAAKjAAAQIAEGYDAFAAEAAAAAAAEAAsGPABwAAQACowAAECABBmAwBgABAAAAAAABAAHBZwAuAAEAAqMAARoAAQgDAAKjAFyyUqdcirP/lXkDbmljAmZyACQ1MMymYxOY4F9HiZb1rs6Cn1/nGfTKGDFpsxMMlhz8kWl9dOZxRuuFuxEQ5k3BTce1eS9pXA0Ua3CMCjxGH1kA3ZI753Smo2aBC5u6Cc3HcxHtSVXgOq3PpM8YAqUlrw3ObDP3mHH45CeweV3mVK9FXbpyQzw9KVBmiwABzG7fGmvZctx8pmsQvjwPLiWhcop8nohhip8q2A4Meymn/kbkjRRLrxGgD/V8Pdy6UXmGnxgBlPuLweFl1D7c3AY1IqfhmDULqPhJMoOXiII63gLFm8Ur75YV9LprEGWKH212PfYxy03+jQ9y0TG3u5+HI7tNiOFn9Fov+9kzvseAzfbBZwAuAAEAAqMAARoAHAgDAAKjAFywo2hciQTElXkDbmljAmZyAItAZLkoaAKnpNJrhr2J9PC2W6fPhNAhFk2ppm/AVHTZKfKIdqBczWzlv6QPudz4OBlL3yf1u20tSovabDFLpr3CH6bu8tJHPBZCE+ve2HBjpAAndv/MZKoWqfL7g1Va3DMhi1YSeU/ipXRQxWmBSfAaZ13e/+VaBlMTj3JK5/2fcnqeS8AKoIMgPIe19H9itHHI5T3FWb2WDgq5gOPwqGJeqV6uO65GPIzG85zFeMFfn1uW34vqiFKUveV6sDUh9d1sWtJJwC0kR0nx9SqGNsHdK8q/Q2PaD/S3wqOd3cEs3rS/ANH04c59VKVOQfsyLR7l+OfNf9cp8aw1veoRcioAACkFmAAAgAAAAA=="}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1552922487.76312,"querytime":1,"answerfrom":"192.134.4.1"}}},"0TlRUFu34T/7a+Z9BxkQVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922501.37072,"querytime":1,"answerfrom":"192.134.4.1","data":"9+6EAAABAAEAAQAAA1dXdwVBRm5pYwJGcgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1552922487.84341,"querytime":1,"answerfrom":"192.134.4.1"}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"answerfrom":"192.134.4.1","timestamp":1552922481.92687,"data":"cGaEAAABAAEAAwAGBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHhX+vgAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zM8AwwHUAAQABAAKjAAAEwIYEAcBjAAEAAQACowAABMBdAATAhwABAAEAAqMAAATAhgAxwHUAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBjABwAAQACowAAECABBmAwBQABAAAAAAABAALAhwAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB"}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":2,"timestamp":1552922486.68736,"answerfrom":"192.134.4.1","data":"Q1KEAAABAAEABQAIA25zMgNuaWMCZnIAABwAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAAoDbnM2A2V4dMAQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnMxwGzAjAABAAEAAqMAAATBM9ANwEQAAQABAAKjAAAEwIYEAcAMAAEAAQACowAABMBdAATAVgABAAEAAqMAAATAhgAxwGgAAQABAAKjAAAEgjsfHcBEABwAAQACowAAECABBnwiGAACAAAAAAAEAAHAVgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwGgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"JdeEAAABAAMAAAAGBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMyA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zM8AqwDwAAQABAAKjAAAEwIYEAcAmAAEAAQACowAABMBdAATATgABAAEAAqMAAATAhgAxwDwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAmABwAAQACowAAECABBmAwBQABAAAAAAABAALATgAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQAB","timestamp":1552922478.37999,"querytime":1,"answerfrom":"192.134.4.1"}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.4.1","querytime":2,"timestamp":1552922486.66256,"data":"Pw2EAAABAAEABQAIA25zMQNuaWMCZnIAAAEAAcAMAAEAAQACowAABMCGBAHAEAACAAEAAqMAAAoDbnMxA2V4dMAQwBAAAgABAAKjAAAGA25zMsAQwBAAAgABAAKjAAAGA25zM8AQwBAAAgABAAKjAAACwAzAEAACAAEAAqMAAAYDbnM2wDzAOAABAAEAAqMAAATBM9ANwE4AAQABAAKjAAAEwF0ABMBgAAEAAQACowAABMCGADHAgAABAAEAAqMAAASCOx8dwAwAHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcBOABwAAQACowAAECABBmAwBQABAAAAAAABAALAYAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwIAAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw=="}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Bl2ABQABAAAAAAAAAAACAAE=","timestamp":1552922487.34253,"querytime":1,"answerfrom":"192.134.4.1"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"77GEAAABAAEABQAIA25zMgNuaWMCZnIAAAEAAcAMAAEAAQACowAABMBdAATAEAACAAEAAqMAAAYDbnMzwBDAEAACAAEAAqMAAALADMAQAAIAAQACowAACgNuczYDZXh0wBDAEAACAAEAAqMAAAYDbnMxwBDAEAACAAEAAqMAAAYDbnMxwFzAgAABAAEAAqMAAATBM9ANwG4AAQABAAKjAAAEwIYEAcA4AAEAAQACowAABMCGADHAWAABAAEAAqMAAASCOx8dwG4AHAABAAKjAAAQIAEGfCIYAAIAAAAAAAQAAcAMABwAAQACowAAECABBmAwBQABAAAAAAABAALAOAAcAAEAAqMAABAgAQZgMAYAAQAAAAAAAQABwFgAHAABAAKjAAAQIAEGIAAAAP8AAAAAAAAALw==","timestamp":1552922486.67861,"querytime":2,"answerfrom":"192.134.4.1"}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","answerfrom":"192.134.4.1","querytime":1,"timestamp":1552922487.68894}}},"KsaIpCVhpeMsuVQal+DZhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922487.52758,"querytime":1,"answerfrom":"192.134.4.1","data":"38eEAAABAAEAAQAAA3dXVwVhZk5pQwJGUgAABgABwAwABQABAAACWAANBmxiMDEtMQNuaWPAFsAxAAYAAQAAFRgALQlkbnNtYXN0ZXLAMQpob3N0bWFzdGVywDF4V/sCAAAcIAAABwgAJOoAAAAVGA=="}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"yryABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","querytime":1,"timestamp":1552922486.96693,"answerfrom":"192.134.4.1"}}}} -m.gtld-servers.net 192.55.83.30 {"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":65,"timestamp":1552922483.75277,"answerfrom":"192.55.83.30","data":"3kKAAAABAAAABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwA4AAgABAAKjAAAPA2F2MQVuc3RsZANjb20AwA4AAgABAAKjAAAGA2F2MsA0wA4AAgABAAKjAAAGA2F2M8A0wA4AAgABAAKjAAAGA2F2NMA0"}}},"sNttT2q5QOS0nfEEkjvGLQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":65,"timestamp":1552922483.02378,"answerfrom":"192.55.83.30","data":"002EAAABAAEADQAKA25ldAAABgABwAwABgABAAADhAA9AWEMZ3RsZC1zZXJ2ZXJzwAwFbnN0bGQMdmVyaXNpZ24tZ3JzA2NvbQBcj7dfAAAHCAAAA4QACTqAAAFRgMAMAAIAAQACowAABAFtwCPADAACAAEAAqMAAAQBaMAjwAwAAgABAAKjAAAEAWLAI8AMAAIAAQACowAABAFpwCPADAACAAEAAqMAAAQBasAjwAwAAgABAAKjAAAEAWvAI8AMAAIAAQACowAAAsAhwAwAAgABAAKjAAAEAWbAI8AMAAIAAQACowAABAFkwCPADAACAAEAAqMAAAQBZcAjwAwAAgABAAKjAAAEAWzAI8AMAAIAAQACowAABAFnwCPADAACAAEAAqMAAAQBY8AjwGoAAQABAAKjAAAEwDdTHsBqABwAAQACowAAECABBQGx+QAAAAAAAAAAADDAegABAAEAAqMAAATANnAewHoAHAABAAKjAAAQIAEFAgjMAAAAAAAAAAAAMMCKAAEAAQACowAABMAhDh7AigAcAAEAAqMAABAgAQUDIx0AAAAAAAAAAgAwwJoAAQABAAKjAAAEwCusHsCaABwAAQACowAAECABBQM5wQAAAAAAAAAAADDAqgABAAEAAqMAAATAME8ewLoAAQABAAKjAAAEwDSyHg=="}}},"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":64,"answerfrom":"192.55.83.30","timestamp":1552922482.75581,"data":"ADiAAAABAAAAAwAACnpvbmVtYXN0ZXIDbmV0AAAGAAHADAACAAEAAqMAAAwDbnMyA25pYwJmcgDADAACAAEAAqMAAAsCbnMDbmljAnNlAMAMAAIAAQACowAABgNuczPARw=="}}},"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"xCWAAAABAAAABAAIA2F2NAVuc3RsZANjb20AAAEAAcAQAAIAAQACowAABgNhdjHAEMAQAAIAAQACowAABgNhdjLAEMAQAAIAAQACowAABgNhdjPAEMAQAAIAAQACowAAAsAMwCsAAQABAAKjAAAEwCqxHsArABwAAQACowAAECABBQABJAAAAAAAAAAAADDAPQABAAEAAqMAAATAKrIewD0AHAABAAKjAAAQIAEFAAElAAAAAAAAAAAAMMBPAAEAAQACowAABMBShR7ATwAcAAEAAqMAABAgAQUAASYAAAAAAAAAAAAwwAwAAQABAAKjAAAEwFKGHsAMABwAAQACowAAECABBQABJwAAAAAAAAAAADA=","querytime":65,"timestamp":1552922483.33459,"answerfrom":"192.55.83.30"}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":65,"answerfrom":"192.55.83.30","timestamp":1552922485.3246,"data":"XbuAAAABAAAAAwAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAaAAIAAQACowAADANuczIDbmljAmZyAMAaAAIAAQACowAACwJucwNuaWMCc2UAwBoAAgABAAKjAAAGA25zM8BV"}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":67,"timestamp":1552922479.99327,"answerfrom":"192.55.83.30","data":"xGmAAAABAAAABAAIAXoEYXJpbgNuZXQAABwAAcAOAAIAAQACowAABgNuczHADsAOAAIAAQACowAABgNuczLADsAOAAIAAQACowAABAF1wA7ADgACAAEAAqMAAAYDbnMzwA7AKAABAAEAAqMAAATH1ABswCgAHAABAAKjAAAQIAEFAAATAAAAAAAAAAABCMA6AAEAAQACowAABMdHAGzAOgAcAAEAAqMAABAgAQUAADEAAAAAAAAAAAEIwEwAHAABAAKjAAAQIAEFAAAUYFAArQAAAAAAAcBMAAEAAQACowAABMw92DLAXAABAAEAAqMAAATHBRpswFwAHAABAAKjAAAQIAEFAACpAAAAAAAAAAABCA=="}}},"WI04J6v9tb8oD+4bFtDPog":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.55.83.30","querytime":65,"timestamp":1552922479.41213,"data":"JC6AAAABAAAABAAIBnRpbm5pZQRhcmluA25ldAAAHAABwBMAAgABAAKjAAAGA25zMcATwBMAAgABAAKjAAAGA25zMsATwBMAAgABAAKjAAAEAXXAE8ATAAIAAQACowAABgNuczPAE8AtAAEAAQACowAABMfUAGzALQAcAAEAAqMAABAgAQUAABMAAAAAAAAAAAEIwD8AAQABAAKjAAAEx0cAbMA/ABwAAQACowAAECABBQAAMQAAAAAAAAAAAQjAUQAcAAEAAqMAABAgAQUAABRgUACtAAAAAAABwFEAAQABAAKjAAAEzD3YMsBhAAEAAQACowAABMcFGmzAYQAcAAEAAqMAABAgAQUAAKkAAAAAAAAAAAEI"}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"fdyAAAABAAAABAAIA2F2NAVuc3RsZANjb20AABwAAcAQAAIAAQACowAABgNhdjHAEMAQAAIAAQACowAABgNhdjLAEMAQAAIAAQACowAABgNhdjPAEMAQAAIAAQACowAAAsAMwCsAAQABAAKjAAAEwCqxHsArABwAAQACowAAECABBQABJAAAAAAAAAAAADDAPQABAAEAAqMAAATAKrIewD0AHAABAAKjAAAQIAEFAAElAAAAAAAAAAAAMMBPAAEAAQACowAABMBShR7ATwAcAAEAAqMAABAgAQUAASYAAAAAAAAAAAAwwAwAAQABAAKjAAAEwFKGHsAMABwAAQACowAAECABBQABJwAAAAAAAAAAADA=","timestamp":1552922483.54195,"querytime":65,"answerfrom":"192.55.83.30"}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"9SGAAAABAAAAAwAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAaAAIAAQACowAADANuczIDbmljAmZyAMAaAAIAAQACowAACwJucwNuaWMCc2UAwBoAAgABAAKjAAAGA25zM8BV","querytime":66,"timestamp":1552922484.98209,"answerfrom":"192.55.83.30"}}},"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Q9iAAAABAAAABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwA4AAgABAAKjAAAPA2F2MQVuc3RsZANjb20AwA4AAgABAAKjAAAGA2F2MsA0wA4AAgABAAKjAAAGA2F2M8A0wA4AAgABAAKjAAAGA2F2NMA0","answerfrom":"192.55.83.30","querytime":64,"timestamp":1552922483.20943}}},"JHIAnHDDut/MIJng8E/fKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"PD6AAAABAAAABAAIBnRpbm5pZQRhcmluA25ldAAAAQABwBMAAgABAAKjAAAGA25zMcATwBMAAgABAAKjAAAGA25zMsATwBMAAgABAAKjAAAEAXXAE8ATAAIAAQACowAABgNuczPAE8AtAAEAAQACowAABMfUAGzALQAcAAEAAqMAABAgAQUAABMAAAAAAAAAAAEIwD8AAQABAAKjAAAEx0cAbMA/ABwAAQACowAAECABBQAAMQAAAAAAAAAAAQjAUQAcAAEAAqMAABAgAQUAABRgUACtAAAAAAABwFEAAQABAAKjAAAEzD3YMsBhAAEAAQACowAABMcFGmzAYQAcAAEAAqMAABAgAQUAAKkAAAAAAAAAAAEI","answerfrom":"192.55.83.30","querytime":65,"timestamp":1552922479.25235}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"riaAAAABAAAABAAIAXoEYXJpbgNuZXQAAAEAAcAOAAIAAQACowAABgNuczHADsAOAAIAAQACowAABgNuczLADsAOAAIAAQACowAABAF1wA7ADgACAAEAAqMAAAYDbnMzwA7AKAABAAEAAqMAAATH1ABswCgAHAABAAKjAAAQIAEFAAATAAAAAAAAAAABCMA6AAEAAQACowAABMdHAGzAOgAcAAEAAqMAABAgAQUAADEAAAAAAAAAAAEIwEwAHAABAAKjAAAQIAEFAAAUYFAArQAAAAAAAcBMAAEAAQACowAABMw92DLAXAABAAEAAqMAAATHBRpswFwAHAABAAKjAAAQIAEFAACpAAAAAAAAAAABCA==","answerfrom":"192.55.83.30","querytime":65,"timestamp":1552922479.83227}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1552922482.11321,"querytime":65,"answerfrom":"192.55.83.30","data":"w3uAAAABAAAAAwAACWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwBYAAgABAAKjAAAMA25zMgNuaWMCZnIAwBYAAgABAAKjAAALAm5zA25pYwJzZQDAFgACAAEAAqMAAAYDbnMzwFE="}}}} -av4.nstld.com 192.82.134.30 {"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":15,"timestamp":1552922483.45314,"answerfrom":"192.82.134.30","data":"U5eEAAABAAEABAAHA2F2NAVuc3RsZANjb20AAAEAAcAMAAEAAQAAASwABMBShh7AEAACAAEAAVGAAALADMAQAAIAAQABUYAABgNhdjLAEMAQAAIAAQABUYAABgNhdjPAEMAQAAIAAQABUYAABgNhdjHAEMAMABwAAQAAASwAECABBQABJwAAAAAAAAAAADDASQABAAEAAAEsAATAKrIewEkAHAABAAABLAAQIAEFAAElAAAAAAAAAAAAMMBbAAEAAQAAASwABMBShR7AWwAcAAEAAAEsABAgAQUAASYAAAAAAAAAAAAwwG0AAQABAAABLAAEwCqxHsBtABwAAQAAASwAECABBQABJAAAAAAAAAAAADA="}}},"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":14,"timestamp":1552922483.92013,"answerfrom":"192.82.134.30","data":"xMKEAAABAAEABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwAwAHAABAAFRgAAQIAEFA6g+AAAAAAAAAAIAMMAOAAIAAQABUYAADwNhdjQFbnN0bGQDY29tAMAOAAIAAQABUYAABgNhdjLAUMAOAAIAAQABUYAABgNhdjHAUMAOAAIAAQABUYAABgNhdjPAUA=="}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.82.134.30","querytime":15,"timestamp":1552922483.64418,"data":"eXaEAAABAAEABAAHA2F2NAVuc3RsZANjb20AABwAAcAMABwAAQAAASwAECABBQABJwAAAAAAAAAAADDAEAACAAEAAVGAAALADMAQAAIAAQABUYAABgNhdjLAEMAQAAIAAQABUYAABgNhdjPAEMAQAAIAAQABUYAABgNhdjHAEMAMAAEAAQAAASwABMBShh7AVQABAAEAAAEsAATAKrIewFUAHAABAAABLAAQIAEFAAElAAAAAAAAAAAAMMBnAAEAAQAAASwABMBShR7AZwAcAAEAAAEsABAgAQUAASYAAAAAAAAAAAAwwHkAAQABAAABLAAEwCqxHsB5ABwAAQAAASwAECABBQABJAAAAAAAAAAAADA="}}},"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"8WuEAAABAAEABAAAAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwAwAAQABAAFRgAAEwAUGHsAOAAIAAQABUYAADwNhdjQFbnN0bGQDY29tAMAOAAIAAQABUYAABgNhdjLARMAOAAIAAQABUYAABgNhdjHARMAOAAIAAQABUYAABgNhdjPARA==","timestamp":1552922483.6803,"querytime":15,"answerfrom":"192.82.134.30"}}}} -av4.nstld.com 2001:0500:0127:0000:0000:0000:0000:0030 {"KoOHMqlriAzfyyS3tJUVhQ":null,"Hx/wBzZ7dxT0btOz2sXTow":null} +ns3.arin.net 199.5.26.108 {} +ns3.nic.fr 192.134.0.49 {"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875450.3536,"data":"4CKEAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"192.134.0.49"}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875454.28776,"data":"Um2EAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.0.49"}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"ouKEAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","timestamp":1626875449.06866,"querytime":42}}},"e5t/JY6QxhgCIIAKZ3VBzw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.76717,"querytime":42,"answerfrom":"192.134.0.49","data":"hTCEAAABAAAAAQAAA1d3dwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"ffGEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.0.49","querytime":42,"timestamp":1626875453.70228}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875440.50375,"querytime":42,"answerfrom":"192.134.0.49","data":"GbCEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zMcAq"}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875442.62736,"querytime":43,"answerfrom":"192.134.0.49","data":"8nuEAAABAAEAAwAAATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMgNuaWMCZnIAwDQAAgABAAKjAAACwGbANAACAAEAAqMAAAYDbnMxwGrANAACAAEAAqMAAAYDbnMzwGo="}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875453.37136,"querytime":42,"answerfrom":"192.134.0.49","data":"irGABQABAAAAAAAAAAACAAE="}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875443.34763,"data":"NSGEAAABAAEAAwAAATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMwNuaWMCZnIAwDQAAgABAAKjAAAGA25zMcBqwDQAAgABAAKjAAACwGbANAACAAEAAqMAAAYDbnMywGo=","answerfrom":"192.134.0.49"}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"ZdaEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","timestamp":1626875449.62026,"querytime":42}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"bgqABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ==","timestamp":1626875450.63933,"querytime":42}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875442.27514,"querytime":42,"answerfrom":"192.134.0.49","data":"LzaEAAABAAEAAwAAATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHADAAMAAEAAqMAAAwDbnMyA25pYwJmcgDADgACAAEAAqMAAAYDbnMzwDnADgACAAEAAqMAAALANcAOAAIAAQACowAABgNuczHAOQ=="}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875449.67705,"querytime":42,"answerfrom":"192.134.0.49","data":"mSiEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMxA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zM8AqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA"}}},"3aY5XkMnVsoC/UHcIq6pjg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875473.7126,"querytime":42,"answerfrom":"192.134.0.49","data":"BdyEAAABAAAAAQAAA3d3dwVBRk5pQwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875443.86479,"querytime":85,"answerfrom":"192.134.0.49","data":"3TWEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zMsAw"}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","timestamp":1626875454.56687,"querytime":42}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":43,"timestamp":1626875442.99304,"data":"p1eEAAABAAEAAwAAAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAMAAwAAQACowAADANuczMDbmljAmZyAMAPAAIAAQACowAABgNuczHAO8APAAIAAQACowAABgNuczLAO8APAAIAAQACowAAAsA3","answerfrom":"192.134.0.49"}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875454.89112,"querytime":42,"answerfrom":"192.134.0.49","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"1aWEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.0.49","querytime":42,"timestamp":1626875454.23172}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"LB+EAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875451.74125,"querytime":42}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"RcuEAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczLARg==","timestamp":1626875452.96925,"querytime":43}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875441.92385,"querytime":42,"answerfrom":"192.134.0.49","data":"HzOEAAABAAEAAwAAATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwAwADAABAAKjAAAMA25zMQNuaWMCZnIAwA4AAgABAAKjAAAGA25zMsA6wA4AAgABAAKjAAACwDbADgACAAEAAqMAAAYDbnMzwDo="}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"kfSABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","timestamp":1626875450.69408,"querytime":42}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"eRiEAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","timestamp":1626875450.30158,"querytime":42}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875448.41184,"querytime":42,"answerfrom":"192.134.0.49","data":"mwaEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAQMNJj7sU0efcgyyjrOV9l2LSJUGdzly/DxDP9REsSOSrmdLtH2gsEr1P4bi03bX4LoFqE86cBMqbLr3kJwXs6CGP8AMADAAAQACowAARAEAAw16la/WEOlEzuewhOJzcvfGr7csw5iDJLtIrSvwdLD6/UVtNoPBzwlLpUA0oarvHfwuvWbUNfbXvs+h9DT3B4ZlwAwAMAABAAKjAABEAQADDfTsADVb1Qk1ZeCY/+UliZAm1DsOMVxH7Z6oirct66IJ/49rRuj5l2G1kyYQKF6vexrFshX517Gaf3mg2NXPk+jADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","timestamp":1626875455.3021,"querytime":42}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"zjWEAAABAAEAAwAAATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwAwADAABAAKjAAAMA25zMQNuaWMCZnIAwDQAAgABAAKjAAAGA25zMsBqwDQAAgABAAKjAAAGA25zM8BqwDQAAgABAAKjAAACwGY=","answerfrom":"192.134.0.49","querytime":42,"timestamp":1626875441.29324}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"qFWEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMQNuaWPAEsAMAAIAAQACowAABgNuczPAOsAMAAIAAQACowAABgNuczLAOg==","timestamp":1626875452.92124,"querytime":42}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":43,"timestamp":1626875450.58544,"data":"h2qABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"192.134.0.49"}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.134.0.49","data":"A3OEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAw","timestamp":1626875440.45305,"querytime":42}}},"lPK6iO4hY+qisxgYHMEdqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":42,"timestamp":1626875473.42158,"data":"4wSEAAABAAAAAQAAA3dXVwVhRk5pQwJmcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"192.134.0.49"}}}} +ns3.nic.fr 2001:0660:3006:0001:0000:0000:0001:0001 {"xYsWicHjyDLj3FqakLvglA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.45289,"data":"g4eEAAABAAAABQABBWFmbmljAmZyAAA8AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"2001:660:3006:1::1:1"}}},"goXMsk806GOykQOVphsxqQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"FKOEAAABAAAAAQAAA3d3dwVBZk5pQwJGUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","answerfrom":"2001:660:3006:1::1:1","querytime":35,"timestamp":1626875454.39131}}},"iTxdshaVSlo2CXRTeL7dlA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"7++EAwABAAAACwABEnh4LS10ZXN0LXRlc3QtdGVzdAVhZm5pYwJmcgAAAQABwB8ABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAJQpob3N0bWFzdGVywEN4dxzkAAAcIAAABwgAJOoAAAAVGMAfAC4AAQAAFRgAXAAGDQIAAqMAYR/vt2D38kJBWAVhZm5pYwJmcgC0kTQV95bWOM0GzU1Hv5J2BXBaIpiLvqKDHkos1ntFYrBJ515394dTYeyPAUogc88PTwkOIiPk/SMRzU+SJR9KIDVrbWpqY25lOTNxb25xc29lazRqa2lyZHJpczUwYzdhwB8AMgABAAAVGAArAQEAAQhXLpUm3A9EwBQzY99LLGsA6DTiBXxxClOokQWOmAAHYgGACAACkMDSAC4AAQAAFRgAXAAyDQMAABUYYRSHx2DsaMtBWAVhZm5pYwJmcgDL/aikt35wcM4B5ldFj9CeJd41r9TmnLHfwJDyh7y5w78qmuaoZjQBO/39hTpRMKUBg1KYhHzDhodmGNhdI+wVwNIALgABAAAVGABcADINAwAAFRhhFIfHYOxoy6hmBWFmbmljAmZyAOdEvlPkd1VkrZTO+p3EHt1iTSgfPjztB50pg42qg6lGEHEVku+1of0JqAHWIRWADVI5JWD0iJQndFRmX3UR3V4gOGc3MHNzbmo2Nm80NDV0M29kMGQwcW12MTd1ZmZnNm3AHwAyAAEAABUYACoBAQABCFculSbcD0TAFE8IhA3t1IEhzo3CFY7NkGHh9j19AAZAAAAAAALB+gAuAAEAABUYAFwAMg0DAAAVGGEUn7Vg7GjLQVgFYWZuaWMCZnIAlzxdLIrIxdKIH6Q9xvjl4OpqJ5Q4zpUSKoHovAzQzNF1u+gfNhb1hxhSZCjOkebdE8YkEV6CaPisznvfeG95RcH6AC4AAQAAFRgAXAAyDQMAABUYYRSftWDsaMuoZgVhZm5pYwJmcgCE/5yNllhZe17w8AW5yTgn4UCNwPiWBQrQ2IeF+7uV23GgWs5AlCnm0EytmPSYR1WxCrDGxNn078uuPD29pOc0IGxhN25uMGttZjZmazdmMGU5NTJtNDFiZmN2OHF2NjkxwB8AMgABAAAVGAAqAQEAAQhXLpUm3A9EwBSx/gRueyI2aKfRnZMSlrTZDOAksAAGBAAAAAACwyEALgABAAAVGABcADINAwAAFRhhFXMrYO1JzUFYBWFmbmljAmZyAJsxXF/gzh3OErkhM0Mz11TQiGehGLvKOTNZWXhATW2E5ueuH+PZe1MKCFGChKPXw03WmEjqmYNDAlo3mjbCQ2LDIQAuAAEAABUYAFwAMg0DAAAVGGEVcytg7UnNqGYFYWZuaWMCZnIAz3QqghRuU2aQlY1FMvokUGO9HZAH1LVMb7IL/giSFNDobfluIcDerWOjyeifgGxirrUY+64Xv9pTyshRnB/+DQAAKQWYAACAAAAA","answerfrom":"2001:660:3006:1::1:1","querytime":35,"timestamp":1626875449.12717}}},"TJ/AJOD5oSy8le0E7lFXTg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875440.563,"data":"6OaEAAABAAMAAAAABWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMcAqwAwAAgABAAKjAAAGA25zMsAq","answerfrom":"2001:660:3006:1::1:1"}}},"nHMR2dZciQCKXE3VKQ+3Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3006:1::1:1","data":"vECEAAABAAAAAQAAA3dXVwVBZk5pYwJmUgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA==","timestamp":1626875453.75669,"querytime":35}}},"UzfnoYKACE71u6V/QQ17nw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875453.42335,"data":"dciABQABAAAAAAAAAAACAAE=","answerfrom":"2001:660:3006:1::1:1"}}},"O68ltp376WF7LmvuFanZvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"X0iEAAABAAIABQABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAKjAABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0rADAACAAEAAqMAAAYDbnMzwDDADAACAAEAAqMAAAYDbnMywDDADAACAAEAAqMAAAYDbnMxwDDADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1QVgFYWZuaWMCZnIA5F5Z8PLMPXS4Nm7R+S4E7xFqf4D+xzlRKLUzx+ZVBbs4g8bB3k+Ee4j5XSewNvp3BivS8M/Unr1K0fvA1opI4cAMAC4AAQACowAAXAACDQIAAqMAYRoNwWDxyvWoZgVhZm5pYwJmcgCdbvUc/nZXUjicb0878fqZiofP2R4x2VsTXJSvvvU08JR4sVFD88RB7CZhSbqIxUqfLtVOerBk2IIYavjS+WKMAAApBZgAAIAAAAA=","answerfrom":"2001:660:3006:1::1:1","querytime":35,"timestamp":1626875449.73433}}},"u2IVdHOj+hP1WgRzMMG0Hg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875450.78921,"querytime":35,"answerfrom":"2001:660:3006:1::1:1","data":"xa6ABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdAVpY2FubgNvcmcAAAEAAQ=="}}},"OQfuvtCciSqkJYd+eIwdqA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875449.78208,"data":"pMSEAAABAAUAAAABBWFmbmljAmZyAAACAAHADAACAAEAAqMAAAoDbnMzA25pY8ASwAwAAgABAAKjAAAGA25zMsAqwAwAAgABAAKjAAAGA25zMcAqwAwALgABAAKjAABcAAINAgACowBhGg3BYPHK9UFYBWFmbmljAmZyAOReWfDyzD10uDZu0fkuBO8Ran+A/sc5USi1M8fmVQW7OIPGwd5PhHuI+V0nsDb6dwYr0vDP1J69StH7wNaKSOHADAAuAAEAAqMAAFwAAg0CAAKjAGEaDcFg8cr1qGYFYWZuaWMCZnIAnW71HP52V1I4nG9PO/H6mYqHz9keMdlbE1yUr771NPCUeLFRQ/PEQewmYUm6iMVKny7VTnqwZNiCGGr40vlijAAAKQWYAACAAAAA","answerfrom":"2001:660:3006:1::1:1"}}},"G5mY7b4q1l50Mf6YfNGUtg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3006:1::1:1","data":"YViEAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zM8AwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zMcAw","timestamp":1626875443.96073,"querytime":72}}},"zueeZkdHfWptXkiyz6gmOA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3006:1::1:1","data":"AACAAAABAAAAAAABBWFmbmljAmZyAAAGAAEAACkFmAEAAAAAAA==","timestamp":1626875454.62226,"querytime":34}}},"cgyeMZwmsWADV4zvkMbarg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":36,"timestamp":1626875454.99858,"data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"2001:660:3006:1::1:1"}}},"JE4OvF401bS3t6rlC5qYVQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875454.34694,"querytime":35,"answerfrom":"2001:660:3006:1::1:1","data":"50WEAAABAAAAAQAAA1d3VwVhRk5pYwJGcgAABgABwBAABgABAAAVGAAxCWRuc21hc3RlcgNuaWPAFgpob3N0bWFzdGVywDR4dxzkAAAcIAAABwgAJOoAAAAVGA=="}}},"pOu0bQGV1Z/qItEzagW7FA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875451.79391,"data":"18yEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA=","answerfrom":"2001:660:3006:1::1:1"}}},"2/fg0ozhiGSsmWtVHzHJow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3006:1::1:1","data":"B4KEAAABAAEAAwAABWFmbmljAmZyAAAcAAHADAAcAAEAAAJYABAgAQZ8IhgDAgAAAAAAUQIxwAwAAgABAAKjAAAKA25zMgNuaWPAEsAMAAIAAQACowAABgNuczPARsAMAAIAAQACowAABgNuczHARg==","timestamp":1626875453.06876,"querytime":35}}},"auoc0YjagPcFkyMHmxRk1g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.83794,"data":"xMiABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdARyaXBlA25ldAAAAQAB","answerfrom":"2001:660:3006:1::1:1"}}},"CFtsJvzdNZr5xrBiWkborQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"TLyEAAABAAQAAAABBWFmbmljAmZyAAAwAAHADAAwAAEAAqMAAEQBAAMNepWv1hDpRM7nsITic3L3xq+3LMOYgyS7SK0r8HSw+v1FbTaDwc8JS6VANKGq7x38Lr1m1DX2177PofQ09weGZcAMADAAAQACowAARAEAAw307AA1W9UJNWXgmP/lJYmQJtQ7DjFcR+2eqIq3LeuiCf+Pa0bo+ZdhtZMmECher3saxbIV+dexmn95oNjVz5PowAwAMAABAAKjAABEAQEDDSY+7FNHn3IMso6zlfZdi0iVBnc5cvw8Qz/URLEjkq5nS7R9oLBK9T+G4tN21+C6BahPOnATKmy695CcF7Oghj/ADAAuAAEAAqMAAFwAMA0CAAKjAGEObBJg5qQ7VWIFYWZuaWMCZnIAVx5yCXbFJ2LFkrX4eAw1yTP1PFk7uB5lCeBXHC7N3BxlO7hPL6hro39wopon9FXJU1p0+/aTD1dKDUF4Ba6cKQAAKQWYAACAAAAA","answerfrom":"2001:660:3006:1::1:1","querytime":35,"timestamp":1626875448.46138}}},"mVEPLhQg5DRW37JRLGghsA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875450.40503,"data":"Js6EAAABAAAABQABBWFmbmljAmZyAAA7AAHADAAGAAEAABUYADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwALgABAAAVGABcAAYNAgACowBhH++3YPfyQkFYBWFmbmljAmZyALSRNBX3ltY4zQbNTUe/knYFcFoimIu+ooMeSizWe0VisEnnXnf3h1Nh7I8BSiBzzw9PCQ4iI+T9IxHNT5IlH0ogNWttampjbmU5M3FvbnFzb2VrNGpraXJkcmlzNTBjN2HADAAyAAEAABUYACsBAQABCFculSbcD0TAFDNj30ssawDoNOIFfHEKU6iRBY6YAAdiAYAIAAKQwL8ALgABAAAVGABcADINAwAAFRhhFIfHYOxoy0FYBWFmbmljAmZyAMv9qKS3fnBwzgHmV0WP0J4l3jWv1Oacsd/AkPKHvLnDvyqa5qhmNAE7/f2FOlEwpQGDUpiEfMOGh2YY2F0j7BXAvwAuAAEAABUYAFwAMg0DAAAVGGEUh8dg7GjLqGYFYWZuaWMCZnIA50S+U+R3VWStlM76ncQe3WJNKB8+PO0HnSmDjaqDqUYQcRWS77Wh/QmoAdYhFYANUjklYPSIlCd0VGZfdRHdXgAAKQWYAACAAAAA","answerfrom":"2001:660:3006:1::1:1"}}},"nys7lrpg8L2EaHBxy2MfKg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.16144,"querytime":35,"answerfrom":"2001:660:3006:1::1:1","data":"AACEAAABAAEAAAABBWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYAAApBZgAAAAAAAA="}}},"qL/NiXWWrnxHWt/3WBvP2w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":34,"timestamp":1626875453.02105,"data":"uXOEAAABAAEAAwAABWFmbmljAmZyAAABAAHADAABAAEAAAJYAATAhgUlwAwAAgABAAKjAAAKA25zMwNuaWPAEsAMAAIAAQACowAABgNuczLAOsAMAAIAAQACowAABgNuczHAOg==","answerfrom":"2001:660:3006:1::1:1"}}},"F8IuiH+hJkzDv9m0t6ioyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Q5KABQABAAAAAAAAEnhuLS1uYW1lc2VydmVydGVzdANpaXMCc2UAAAEAAQ==","answerfrom":"2001:660:3006:1::1:1","querytime":35,"timestamp":1626875450.74565}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:660:3006:1::1:1","data":"Nt2EAAABAAEAAwAABWFmbmljAmZyAAAGAAHADAAGAAEAAqMAADEJZG5zbWFzdGVyA25pY8ASCmhvc3RtYXN0ZXLAMHh3HOQAABwgAAAHCAAk6gAAABUYwAwAAgABAAKjAAAGA25zMcAwwAwAAgABAAKjAAAGA25zMsAwwAwAAgABAAKjAAAGA25zM8Aw","timestamp":1626875437.07228,"querytime":35}}}} +g.gtld-servers.net 192.42.93.30 {} +e.root-servers.net 2001:0500:00a8:0000:0000:0000:0000:000e {} +e.root-servers.net 192.203.230.10 {} +google-public-dns-a.google.com 8.8.8.8 {"F8o4iqNL5YNIxp/229fl4A":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.58378,"querytime":11,"answerfrom":"8.8.8.8","data":"IJ2BgAABAAIAAAAAA25pYwJmcgAADwABwAwADwABAABTCwAIABQDbXg0wAzADAAPAAEAAFMLAAgACQNteDXADA=="}}},"9PN8RA7cWRAanZkHnZfyfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.61706,"querytime":55,"answerfrom":"8.8.8.8","data":"6HmBgAABAAEAAAAAA214NANuaWMCZnIAABwAAcAMABwAAQAAVF8AECABBnwiGAACAAAAAAAEABI="}}},"tPXHqNMPcZ1Ux0ZgbmKdvQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"8.8.8.8","data":"9A+BgAABAAEAAAAAA214NQNuaWMCZnIAABwAAcAMABwAAQAAVF8AECABBnwiGAACAAAAAAAEABM=","timestamp":1626875455.70361,"querytime":59}}},"+NAI2Wz9QDzqq2P7UybQeQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"8.8.8.8","data":"E5KBgAABAAEAAAAAA214NANuaWMCZnIAAAEAAcAMAAEAAQAAUwsABMCGBAw=","timestamp":1626875455.60038,"querytime":11}}},"0XkzvLCnO8Y7IyxM5lLiyw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875455.68546,"querytime":11,"answerfrom":"8.8.8.8","data":"DC2BgAABAAEAAAAAA214NQNuaWMCZnIAAAEAAcAMAAEAAQAAUwsABMCGBA0="}}}} +ns2.asnlookup.zonemaster.net 45.155.96.70 {"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875444.38593,"querytime":1,"answerfrom":"45.155.96.70","data":"KLCEAAABAAEAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwAwABgABAAACWAAiA25zMcAMBWFkbWluwAxg9/eEAAAHCAAAA4QAAVGAAAADhMAMAAIAAQAAAlgAAsA2wAwAAgABAAACWAAGA25zMsAMwDYAAQABAAACWAAELZtgRcByAAEAAQAAAlgABC2bYEY="}}},"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.98464,"querytime":1,"answerfrom":"45.155.96.70","data":"g2yEAAABAAEAAgACA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAMAAEAAQAAAlgABC2bYEXAEAACAAEAAAJYAAYDbnMywBDAEAACAAEAAAJYAALADMAMAAEAAQAAAlgABC2bYEXASgABAAEAAAJYAAQtm2BG"}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875446.1598,"data":"+L2EAAABAAAAAQAAA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAQAAYAAQAAA4QAHsAMBWFkbWluwBBg9/eEAAAHCAAAA4QAAVGAAAADhA==","answerfrom":"45.155.96.70"}}}} +b.ip6-servers.arpa 2001:0500:0086:0000:0000:0000:0000:0086 {} +b.ip6-servers.arpa 199.253.182.182 {} +m.ns.se 194.0.11.112 {} +ns1.arin.net 199.212.0.108 {} +ns1.arin.net 2001:0500:0013:0000:0000:0000:0000:0108 {} +f.ns.se 192.71.53.53 {} +f.ns.se 2a01:03f0:0000:0305:0000:0000:0000:0053 {} +d.gtld-servers.net 192.31.80.30 {} a.ns.se 2a01:03f0:0000:0301:0000:0000:0000:0053 {} a.ns.se 192.36.144.107 {} -a.gtld-servers.net 192.5.6.30 {"Ox0c7Ezn+YV8Irn2871Liw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":80,"timestamp":1552922483.94655,"answerfrom":"192.5.6.30","data":"lPCEAAABAA0AAAAMA25ldAAAAgABwAwAAgABAAKjAAARAWYMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBZ8AjwAwAAgABAAKjAAAEAWjAI8AMAAIAAQACowAABAFtwCPADAACAAEAAqMAAAQBZMAjwAwAAgABAAKjAAAEAWPAI8AMAAIAAQACowAABAFhwCPADAACAAEAAqMAAAQBYsAjwAwAAgABAAKjAAAEAWrAI8AMAAIAAQACowAABAFlwCPADAACAAEAAqMAAAQBa8AjwAwAAgABAAKjAAAEAWzAI8AMAAIAAQACowAABAFpwCPAIQABAAEAAqMAAATAIzMewCEAHAABAAKjAAAQIAEFA9QUAAAAAAAAAAAAMMA+AAEAAQACowAABMAqXR7APgAcAAEAAqMAABAgAQUD7qMAAAAAAAAAAAAwwE4AAQABAAKjAAAEwDZwHsBOABwAAQACowAAECABBQIIzAAAAAAAAAAAADDAXgABAAEAAqMAAATAN1MewF4AHAABAAKjAAAQIAEFAbH5AAAAAAAAAAAAMMBuAAEAAQACowAABMAfUB7AbgAcAAEAAqMAABAgAQUAhW4AAAAAAAAAAAAwwH4AAQABAAKjAAAEwBpcHsB+ABwAAQACowAAECABBQOD6wAAAAAAAAAAADA="}}},"B17azTRyHl7a9Vo8oZH1fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"192.5.6.30","querytime":78,"timestamp":1552922484.25824,"data":"rSKAAAABAAAAAwAACnpvbmVtYXN0ZXIDbmV0AAACAAHADAACAAEAAqMAAAwDbnMyA25pYwJmcgDADAACAAEAAqMAAAsCbnMDbmljAnNlAMAMAAIAAQACowAABgNuczPARw=="}}}} -a.gtld-servers.net 2001:0503:a83e:0000:0000:0000:0002:0030 {} -h.gtld-servers.net 192.54.112.30 {} -f.root-servers.net 2001:0500:002f:0000:0000:0000:0000:000f {} -f.root-servers.net 192.5.5.241 {} +g.ns.se 130.239.5.114 {} +g.ns.se 2001:06b0:000e:0003:0000:0000:0000:0001 {} +d.in-addr-servers.arpa 200.10.60.53 {} +d.in-addr-servers.arpa 2001:13c7:7010:0000:0000:0000:0000:0053 {} +m.root-servers.net 202.12.27.33 {"lasV0cL3Q8ch+RpJm5zPYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875469.18338,"data":"O1SAAAABAAAADQAOBXJpcm5zBGFyaW4DbmV0AAAcAAHAFwACAAEAAqMAABEBaQxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFqwC7AFwACAAEAAqMAAAQBZMAuwBcAAgABAAKjAAAEAWzALsAXAAIAAQACowAABAFrwC7AFwACAAEAAqMAAAQBYcAuwBcAAgABAAKjAAAEAWfALsAXAAIAAQACowAABAFjwC7AFwACAAEAAqMAAAQBbcAuwBcAAgABAAKjAAAEAWjALsAXAAIAAQACowAABAFlwC7AFwACAAEAAqMAAAQBZsAuwBcAAgABAAKjAAAEAWLALsCJAAEAAQACowAABMAFBh7A+QABAAEAAqMAAATAIQ4ewKkAAQABAAKjAAAEwBpcHsBZAAEAAQACowAABMAfUB7A2QABAAEAAqMAAATADF4ewOkAAQABAAKjAAAEwCMzHsCZAAEAAQACowAABMAqXR7AyQABAAEAAqMAAATANnAewCwAAQABAAKjAAAEwCusHsBJAAEAAQACowAABMAwTx7AeQABAAEAAqMAAATANLIewGkAAQABAAKjAAAEwCmiHsC5AAEAAQACowAABMA3Ux7AiQAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw","answerfrom":"202.12.27.33"}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875470.03419,"data":"RAKAAAABAAAABgAMAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAZAAIAAQACowAAFAFiD2luLWFkZHItc2VydmVyc8AhwBkAAgABAAKjAAAEAWXAOcAZAAIAAQACowAABAFhwDnAGQACAAEAAqMAAAQBY8A5wBkAAgABAAKjAAAEAWbAOcAZAAIAAQACowAABAFkwDnAZwABAAEAAqMAAATHtLY1wDcAAQABAAKjAAAEx/23t8B3AAEAAQACowAABMTYqQrAlwABAAEAAqMAAATICjw1wFcAAQABAAKjAAAEy3dWZcCHAAEAAQACowAABMEACQHAZwAcAAEAAqMAABAmIAA34AAAAAAAAAAAAABTwDcAHAABAAKjAAAQIAEFAACHAAAAAAAAAAAAh8B3ABwAAQACowAAECABQ/gBEAAAAAAAAAAAABDAlwAcAAEAAqMAABAgARPHcBAAAAAAAAAAAABTwFcAHAABAAKjAAAQIAEN2AAGAAAAAAAAAAABAcCHABwAAQACowAAECABBnwA4AAAAAAAAAAAAAE=","answerfrom":"202.12.27.33"}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875469.4811,"querytime":34,"answerfrom":"202.12.27.33","data":"ldmAAAABAAAADQAOAXoEYXJpbgNuZXQAABwAAcATAAIAAQACowAAEQFqDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWXAKsATAAIAAQACowAABAFhwCrAEwACAAEAAqMAAAQBa8AqwBMAAgABAAKjAAAEAW3AKsATAAIAAQACowAABAFmwCrAEwACAAEAAqMAAAQBYsAqwBMAAgABAAKjAAAEAWTAKsATAAIAAQACowAABAFpwCrAEwACAAEAAqMAAAQBZ8AqwBMAAgABAAKjAAAEAWzAKsATAAIAAQACowAABAFjwCrAEwACAAEAAqMAAAQBaMAqwFUAAQABAAKjAAAEwAUGHsCVAAEAAQACowAABMAhDh7A5QABAAEAAqMAAATAGlwewKUAAQABAAKjAAAEwB9QHsBFAAEAAQACowAABMAMXh7AhQABAAEAAqMAAATAIzMewMUAAQABAAKjAAAEwCpdHsD1AAEAAQACowAABMA2cB7AtQABAAEAAqMAAATAK6wewCgAAQABAAKjAAAEwDBPHsBlAAEAAQACowAABMA0sh7A1QABAAEAAqMAAATAKaIewHUAAQABAAKjAAAEwDdTHsBVABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","data":"NFWAAAABAAAABAAIAWUDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWTAEsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFmwA7ADAABAAEAAqMAAATBsJAWwCoAAQABAAKjAAAEwgAJAcBYAAEAAQACowAABMKSai7ASAABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsAqABwAAQACowAAECABBngADAAAAAAAAAAAAAHAWAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","timestamp":1626875470.73897,"querytime":35}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"hhKAAAABAAAABAAIAWcDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZsAOwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAABAFkwBLADAABAAEAAqMAAATCACQBwFgAAQABAAKjAAAEwgAJAcBIAAEAAQACowAABMGwkBbAOAABAAEAAqMAAATCkmouwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAcBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHASAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwDgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"202.12.27.33","querytime":35,"timestamp":1626875471.40325}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"4AiAAAABAAAABAAIA25zMwNuaWMCZnIAABwAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZwNleHTAEMAUAAIAAQACowAABAFmwDrAFAACAAEAAqMAAAQBZcA6wCgAAQABAAKjAAAEwgAJAcBcAAEAAQACowAABMGwkBbATAABAAEAAqMAAATCkmouwDgAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAXAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8A4ABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"202.12.27.33","querytime":35,"timestamp":1626875468.73418}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875468.44983,"querytime":34,"answerfrom":"202.12.27.33","data":"eECAAAABAAAABAAIA25zMgNuaWMCZnIAABwAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZgNleHTAEMAUAAIAAQACowAABAFlwDrAFAACAAEAAqMAAAQBZ8A6wCgAAQABAAKjAAAEwgAJAcBMAAEAAQACowAABMGwkBbAOAABAAEAAqMAAATCkmouwFwAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHATAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwDgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BcABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":34,"timestamp":1626875467.43725,"data":"miyAAAABAAAABAAIBWFmbmljAmZyAAAGAAHAEgACAAEAAqMAAAwBZgNleHQDbmljwBLAEgACAAEAAqMAAAQBZ8AowBIAAgABAAKjAAAEAWXAKMASAAIAAQACowAABAFkwCzAXgABAAEAAqMAAATCAAkBwE4AAQABAAKjAAAEwbCQFsAmAAEAAQACowAABMKSai7APgABAAEAAqMAAATCACQBwF4AHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBOABwAAQACowAAECoADXgAAAECAZMBdgFEACLAJgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwD4AHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"202.12.27.33"}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875468.19746,"querytime":35,"answerfrom":"202.12.27.33","data":"COqAAAABAAAABAAIA25zMQNuaWMCZnIAABwAAcAUAAIAAQACowAACAFmA2V4dMAQwBQAAgABAAKjAAAEAWXAKsAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZMAQwFwAAQABAAKjAAAEwgAJAcA8AAEAAQACowAABMGwkBbAKAABAAEAAqMAAATCkmouwEwAAQABAAKjAAAEwgAkAcBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHAPAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"oKKAAAABAAAABgAMATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWMLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFkwGjATAACAAEAAqMAAAQBYsBowEwAAgABAAKjAAAEAWHAaMBMAAIAAQACowAABAFmwGjATAACAAEAAqMAAAQBZcBowKIAAQABAAKjAAAEx7S2NcCSAAEAAQACowAABMf9trbAZgABAAEAAqMAAATE2KkLwIIAAQABAAKjAAAEyAdWNcDCAAEAAQACowAABMt3VmXAsgABAAEAAqMAAATBAAkCwKIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CSABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAZgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwIIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8DCABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAsgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","answerfrom":"202.12.27.33","querytime":34,"timestamp":1626875469.02071}}},"Y/IeIwIJb12zVtYwf7MnYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"MQOEAwABAAAAAQAAAnhhAAAGAAEAAAYAAQAAAAAAQAFhDHJvb3Qtc2VydmVycwNuZXQABW5zdGxkDHZlcmlzaWduLWdycwNjb20AeHcc5QAABwgAAAOEAAk6gAABUYA=","answerfrom":"202.12.27.33","querytime":35,"timestamp":1626875475.04191}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":34,"timestamp":1626875471.06799,"data":"leKAAAABAAAABAAIAWYDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAABAFnwA7AFgACAAEAAqMAAAQBZMASwBYAAgABAAKjAAACwAzADAABAAEAAqMAAATCkmouwEoAAQABAAKjAAAEwgAJAcAqAAEAAQACowAABMGwkBbAOgABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BKABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwDoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"202.12.27.33"}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","data":"0QaAAAABAAAABAAIAWQDbmljAmZyAAABAAHAEgACAAEAAqMAAAgBZwNleHTADsASAAIAAQACowAABAFlwCjAEgACAAEAAqMAAAQBZsAowBIAAgABAAKjAAACwAzADAABAAEAAqMAAATCAAkBwDoAAQABAAKjAAAEwbCQFsBKAAEAAQACowAABMKSai7AJgABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcA6ABwAAQACowAAECoADXgAAAECAZMBdgFEACLASgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCYAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","timestamp":1626875467.65076,"querytime":34}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875468.57672,"data":"HYGAAAABAAAABAAIA25zMwNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFmA2V4dMAQwBQAAgABAAKjAAAEAWXAKsAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZMAQwFwAAQABAAKjAAAEwgAJAcA8AAEAAQACowAABMGwkBbAKAABAAEAAqMAAATCkmouwEwAAQABAAKjAAAEwgAkAcBcABwAAQACowAAECABBngADAAAAAAAAAAAAAHAPAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"202.12.27.33"}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875467.53582,"data":"a++AAAABAAAABAAIAmZyAAAGAAHADAACAAEAAqMAAAgBZANuaWPADMAMAAIAAQACowAACAFnA2V4dMAiwAwAAgABAAKjAAAEAWbANsAMAAIAAQACowAABAFlwDbAIAABAAEAAqMAAATCAAkBwFgAAQABAAKjAAAEwbCQFsBIAAEAAQACowAABMKSai7ANAABAAEAAqMAAATCACQBwCAAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBYABwAAQACowAAECoADXgAAAECAZMBdgFEACLASAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwDQAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"202.12.27.33"}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"6diAAAABAAAABAAIAWUDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFkwBLAFgACAAEAAqMAAAQBZsAOwBYAAgABAAKjAAACwAzADAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwAwAAQABAAKjAAAEwbCQFsA6AAEAAQACowAABMIACQHASgABAAEAAqMAAATCkmouwCoAAQABAAKjAAAEwgAkAcA6ABwAAQACowAAECABBngADAAAAAAAAAAAAAHASgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"202.12.27.33","querytime":35,"timestamp":1626875470.90454}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875469.31921,"querytime":34,"answerfrom":"202.12.27.33","data":"h1GAAAABAAAABgAMATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBgAAgABAAKjAAAUAWMPaW4tYWRkci1zZXJ2ZXJzwCDAGAACAAEAAqMAAAQBZcA4wBgAAgABAAKjAAAEAWHAOMAYAAIAAQACowAABAFkwDjAGAACAAEAAqMAAAQBYsA4wBgAAgABAAKjAAAEAWbAOMBmAAEAAQACowAABMe0tjXAhgABAAEAAqMAAATH/be3wDYAAQABAAKjAAAExNipCsB2AAEAAQACowAABMgKPDXAVgABAAEAAqMAAATLd1ZlwJYAAQABAAKjAAAEwQAJAcBmABwAAQACowAAECYgADfgAAAAAAAAAAAAAFPAhgAcAAEAAqMAABAgAQUAAIcAAAAAAAAAAACHwDYAHAABAAKjAAAQIAFD+AEQAAAAAAAAAAAAEMB2ABwAAQACowAAECABE8dwEAAAAAAAAAAAAFPAVgAcAAEAAqMAABAgAQ3YAAYAAAAAAAAAAAEBwJYAHAABAAKjAAAQIAEGfADgAAAAAAAAAAAAAQ=="}}},"UBMcusig6w3synYwbp7omA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875469.10848,"data":"Br6AAAABAAAADQAOBXJpcm5zBGFyaW4DbmV0AAABAAHAFwACAAEAAqMAABEBYQxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFiwC7AFwACAAEAAqMAAAQBa8AuwBcAAgABAAKjAAAEAWjALsAXAAIAAQACowAABAFqwC7AFwACAAEAAqMAAAQBacAuwBcAAgABAAKjAAAEAWXALsAXAAIAAQACowAABAFnwC7AFwACAAEAAqMAAAQBbcAuwBcAAgABAAKjAAAEAWzALsAXAAIAAQACowAABAFjwC7AFwACAAEAAqMAAAQBZMAuwBcAAgABAAKjAAAEAWbALsAsAAEAAQACowAABMAFBh7ASQABAAEAAqMAAATAIQ4ewNkAAQABAAKjAAAEwBpcHsDpAAEAAQACowAABMAfUB7AmQABAAEAAqMAAATADF4ewPkAAQABAAKjAAAEwCMzHsCpAAEAAQACowAABMAqXR7AaQABAAEAAqMAAATANnAewIkAAQABAAKjAAAEwCusHsB5AAEAAQACowAABMAwTx7AWQABAAEAAqMAAATANLIewMkAAQABAAKjAAAEwCmiHsC5AAEAAQACowAABMA3Ux7ALAAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw","answerfrom":"202.12.27.33"}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875469.83232,"querytime":34,"answerfrom":"202.12.27.33","data":"LViAAAABAAAABgAMATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWMLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFkwGjATAACAAEAAqMAAAQBZsBowEwAAgABAAKjAAAEAWHAaMBMAAIAAQACowAABAFlwGjATAACAAEAAqMAAAQBYsBowKIAAQABAAKjAAAEx7S2NcDCAAEAAQACowAABMf9trbAZgABAAEAAqMAAATE2KkLwIIAAQABAAKjAAAEyAdWNcCyAAEAAQACowAABMt3VmXAkgABAAEAAqMAAATBAAkCwKIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8DCABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAZgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwIIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CyABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAkgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC"}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"kLSAAAABAAAABAAIA25zMgNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFmA2V4dMAQwBQAAgABAAKjAAAEAWTAEMAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZcAqwDwAAQABAAKjAAAEwgAJAcBcAAEAAQACowAABMGwkBbAKAABAAEAAqMAAATCkmouwEwAAQABAAKjAAAEwgAkAcA8ABwAAQACowAAECABBngADAAAAAAAAAAAAAHAXAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"202.12.27.33","querytime":34,"timestamp":1626875468.32793}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875467.82895,"data":"UESAAAABAAAABAAIAWQDbmljAmZyAAAcAAHAEgACAAEAAqMAAAgBZQNleHTADsASAAIAAQACowAAAsAMwBIAAgABAAKjAAAEAWbAKMASAAIAAQACowAABAFnwCjADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAABwAwAAQABAAKjAAAEwgAJAcAmAAEAAQACowAABMGwkBbASAABAAEAAqMAAATCkmouwFgAAQABAAKjAAAEwgAkAcAmABwAAQACowAAECoADXgAAAECAZMBdgFEACLASAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwFgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"202.12.27.33"}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":34,"timestamp":1626875468.07573,"data":"z0aAAAABAAAABAAIA25zMQNuaWMCZnIAAAEAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZwNleHTAEMAUAAIAAQACowAABAFmwDrAFAACAAEAAqMAAAQBZcA6wCgAAQABAAKjAAAEwgAJAcBcAAEAAQACowAABMGwkBbATAABAAEAAqMAAATCkmouwDgAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAXAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8A4ABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"202.12.27.33"}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":34,"timestamp":1626875474.20799,"data":"r6aAAAABAAAABAAICWRuc21hc3RlcgNuaWMCZnIAAAEAAcAaAAIAAQACowAACAFnA2V4dMAWwBoAAgABAAKjAAAEAWTAFsAaAAIAAQACowAABAFlwDDAGgACAAEAAqMAAAQBZsAwwEIAAQABAAKjAAAEwgAJAcBSAAEAAQACowAABMGwkBbAYgABAAEAAqMAAATCkmouwC4AAQABAAKjAAAEwgAkAcBCABwAAQACowAAECABBngADAAAAAAAAAAAAAHAUgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8AuABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"202.12.27.33"}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875471.57119,"querytime":35,"answerfrom":"202.12.27.33","data":"oNSAAAABAAAABAAIAWcDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWbADsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWTAEsAWAAIAAQACowAABAFlwA7ADAAcAAEAAqMAABAgAQZ4AEwAAAAAAAAAAAABwAwAAQABAAKjAAAEwgAkAcBIAAEAAQACowAABMIACQHAWAABAAEAAqMAAATBsJAWwCoAAQABAAKjAAAEwpJqLsBIABwAAQACowAAECABBngADAAAAAAAAAAAAAHAWAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCoAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw=="}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875469.62221,"querytime":34,"answerfrom":"202.12.27.33","data":"eXOAAAABAAAABgAMATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAFwACAAEAAqMAABQBYQ9pbi1hZGRyLXNlcnZlcnPAH8AXAAIAAQACowAABAFlwDfAFwACAAEAAqMAAAQBZsA3wBcAAgABAAKjAAAEAWPAN8AXAAIAAQACowAABAFkwDfAFwACAAEAAqMAAAQBYsA3wDUAAQABAAKjAAAEx7S2NcCVAAEAAQACowAABMf9t7fAdQABAAEAAqMAAATE2KkKwIUAAQABAAKjAAAEyAo8NcBVAAEAAQACowAABMt3VmXAZQABAAEAAqMAAATBAAkBwDUAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CVABwAAQACowAAECABBQAAhwAAAAAAAAAAAIfAdQAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAAQwIUAHAABAAKjAAAQIAETx3AQAAAAAAAAAAAAU8BVABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAZQAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAB"}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"202.12.27.33","data":"5g6AAAABAAAADQAOAXoEYXJpbgNuZXQAAAEAAcATAAIAAQACowAAEQFlDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWnAKsATAAIAAQACowAABAFiwCrAEwACAAEAAqMAAAQBbMAqwBMAAgABAAKjAAAEAWrAKsATAAIAAQACowAABAFhwCrAEwACAAEAAqMAAAQBa8AqwBMAAgABAAKjAAAEAWfAKsATAAIAAQACowAABAFmwCrAEwACAAEAAqMAAAQBY8AqwBMAAgABAAKjAAAEAWjAKsATAAIAAQACowAABAFtwCrAEwACAAEAAqMAAAQBZMAqwIUAAQABAAKjAAAEwAUGHsBVAAEAAQACowAABMAhDh7AxQABAAEAAqMAAATAGlwewPUAAQABAAKjAAAEwB9QHsAoAAEAAQACowAABMAMXh7AtQABAAEAAqMAAATAIzMewKUAAQABAAKjAAAEwCpdHsDVAAEAAQACowAABMA2cB7ARQABAAEAAqMAAATAK6wewHUAAQABAAKjAAAEwDBPHsCVAAEAAQACowAABMA0sh7AZQABAAEAAqMAAATAKaIewOUAAQABAAKjAAAEwDdTHsCFABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1626875469.40838,"querytime":35}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875471.22715,"querytime":35,"answerfrom":"202.12.27.33","data":"OEmAAAABAAAABAAIAWYDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWTAEsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFlwA7ADAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwAwAAQABAAKjAAAEwpJqLsAqAAEAAQACowAABMIACQHAWAABAAEAAqMAAATBsJAWwEgAAQABAAKjAAAEwgAkAcAqABwAAQACowAAECABBngADAAAAAAAAAAAAAHAWAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875474.37519,"querytime":34,"answerfrom":"202.12.27.33","data":"cHuAAAABAAAABAAICWRuc21hc3RlcgNuaWMCZnIAABwAAcAaAAIAAQACowAACAFlA2V4dMAWwBoAAgABAAKjAAAEAWbAMMAaAAIAAQACowAABAFkwBbAGgACAAEAAqMAAAQBZ8AwwFIAAQABAAKjAAAEwgAJAcAuAAEAAQACowAABMGwkBbAQgABAAEAAqMAAATCkmouwGIAAQABAAKjAAAEwgAkAcBSABwAAQACowAAECABBngADAAAAAAAAAAAAAHALgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BiABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":35,"timestamp":1626875470.24244,"data":"cpaAAAABAAAABgAMATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWULaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFjwGjATAACAAEAAqMAAAQBYcBowEwAAgABAAKjAAAEAWTAaMBMAAIAAQACowAABAFmwGjATAACAAEAAqMAAAQBYsBowJIAAQABAAKjAAAEx7S2NcDCAAEAAQACowAABMf9trbAggABAAEAAqMAAATE2KkLwKIAAQABAAKjAAAEyAdWNcBmAAEAAQACowAABMt3VmXAsgABAAEAAqMAAATBAAkCwJIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8DCABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAggAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwKIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8BmABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAsgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","answerfrom":"202.12.27.33"}}}} +m.root-servers.net 2001:0dc3:0000:0000:0000:0000:0000:0035 {"qa2ldaRH3Yi2ZSb8/9xaSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"n4WAAAABAAAADQAOA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAAAEAAcAlAAIAAQACowAAEQFrDGd0bGQtc2VydmVyc8AlwCUAAgABAAKjAAAEAW3APMAlAAIAAQACowAABAFhwDzAJQACAAEAAqMAAAQBasA8wCUAAgABAAKjAAAEAWfAPMAlAAIAAQACowAABAFjwDzAJQACAAEAAqMAAAQBacA8wCUAAgABAAKjAAAEAWXAPMAlAAIAAQACowAABAFiwDzAJQACAAEAAqMAAAQBaMA8wCUAAgABAAKjAAAEAWTAPMAlAAIAAQACowAABAFswDzAJQACAAEAAqMAAAQBZsA8wGcAAQABAAKjAAAEwAUGHsDHAAEAAQACowAABMAhDh7AlwABAAEAAqMAAATAGlwewOcAAQABAAKjAAAEwB9QHsC3AAEAAQACowAABMAMXh7BBwABAAEAAqMAAATAIzMewIcAAQABAAKjAAAEwCpdHsDXAAEAAQACowAABMA2cB7ApwABAAEAAqMAAATAK6wewHcAAQABAAKjAAAEwDBPHsA6AAEAAQACowAABMA0sh7A9wABAAEAAqMAAATAKaIewFcAAQABAAKjAAAEwDdTHsBnABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1626875445.82239,"querytime":32}}},"BoP52jf+VIVO3VrSWkuPQQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875437.68595,"querytime":31,"answerfrom":"2001:dc3::35","data":"aKqAAAABAAAABAAIAWUDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFkwBLAFgACAAEAAqMAAALADMAWAAIAAQACowAABAFmwA7ADAABAAEAAqMAAATBsJAWwDoAAQABAAKjAAAEwgAJAcBYAAEAAQACowAABMKSai7AKgABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQKgANeAAAAQIBkwF2AUQAIsA6ABwAAQACowAAECABBngADAAAAAAAAAAAAAHAWAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"7mmtEYchsO9PZT5B8N6/Pg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875442.69182,"data":"IWqAAAABAAAABgAMAjQ5ATADMTM0AzE5Mgdpbi1hZGRyBGFycGEAAAwAAcAZAAIAAQACowAAFAFiD2luLWFkZHItc2VydmVyc8AhwBkAAgABAAKjAAAEAWbAOcAZAAIAAQACowAABAFjwDnAGQACAAEAAqMAAAQBZMA5wBkAAgABAAKjAAAEAWXAOcAZAAIAAQACowAABAFhwDnAlwABAAEAAqMAAATHtLY1wDcAAQABAAKjAAAEx/23t8BnAAEAAQACowAABMTYqQrAdwABAAEAAqMAAATICjw1wIcAAQABAAKjAAAEy3dWZcBXAAEAAQACowAABMEACQHAlwAcAAEAAqMAABAmIAA34AAAAAAAAAAAAABTwDcAHAABAAKjAAAQIAEFAACHAAAAAAAAAAAAh8BnABwAAQACowAAECABQ/gBEAAAAAAAAAAAABDAdwAcAAEAAqMAABAgARPHcBAAAAAAAAAAAABTwIcAHAABAAKjAAAQIAEN2AAGAAAAAAAAAAABAcBXABwAAQACowAAECABBnwA4AAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35"}}},"lasV0cL3Q8ch+RpJm5zPYA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":33,"timestamp":1626875440.98255,"data":"cxWAAAABAAAADQAOBXJpcm5zBGFyaW4DbmV0AAAcAAHAFwACAAEAAqMAABEBYgxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFpwC7AFwACAAEAAqMAAAQBZcAuwBcAAgABAAKjAAAEAWbALsAXAAIAAQACowAABAFnwC7AFwACAAEAAqMAAAQBYcAuwBcAAgABAAKjAAAEAWrALsAXAAIAAQACowAABAFowC7AFwACAAEAAqMAAAQBZMAuwBcAAgABAAKjAAAEAWzALsAXAAIAAQACowAABAFjwC7AFwACAAEAAqMAAAQBbcAuwBcAAgABAAKjAAAEAWvALsCJAAEAAQACowAABMAFBh7ALAABAAEAAqMAAATAIQ4ewNkAAQABAAKjAAAEwBpcHsC5AAEAAQACowAABMAfUB7AWQABAAEAAqMAAATADF4ewGkAAQABAAKjAAAEwCMzHsB5AAEAAQACowAABMAqXR7AqQABAAEAAqMAAATANnAewEkAAQABAAKjAAAEwCusHsCZAAEAAQACowAABMAwTx7A+QABAAEAAqMAAATANLIewMkAAQABAAKjAAAEwCmiHsDpAAEAAQACowAABMA3Ux7AiQAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw","answerfrom":"2001:dc3::35"}}},"/ad1wr3S5M12N8m7PKZMlg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"j+uAAAABAAAABAAIA25zMwNuaWMCZnIAABwAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZwNleHTAEMAUAAIAAQACowAABAFmwDrAFAACAAEAAqMAAAQBZcA6wCgAAQABAAKjAAAEwgAJAcBcAAEAAQACowAABMGwkBbATAABAAEAAqMAAATCkmouwDgAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAXAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8A4ABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35","querytime":33,"timestamp":1626875439.97174}}},"BXVBY8r9sPsznJk1A1ELew":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":31,"timestamp":1626875445.66316,"data":"jXSAAAABAAAACgAPAm5zA25pYwJzZQAAHAABwBMAAgABAAKjAAAHAW0CbnPAE8ATAAIAAQACowAABAFjwCnAEwACAAEAAqMAAAQBZ8ApwBMAAgABAAKjAAAEAXrAKcATAAIAAQACowAABAFmwCnAEwACAAEAAqMAAAQBeMApwBMAAgABAAKjAAAEAWnAKcATAAIAAQACowAABAFhwCnAEwACAAEAAqMAAAQBYsApwBMAAgABAAKjAAAEAXnAKcCaAAEAAQACowAABMAkkGvAqgABAAEAAqMAAATAJIVrwDoAAQABAAKjAAAEwCSHa8BqAAEAAQACowAABMBHNTXASgABAAEAAqMAAASC7wVywIoAAQABAAKjAAAEwpJqFsAnAAEAAQACowAABMIAC3DAegABAAEAAqMAAATVbBkEwLoAAQABAAKjAAAEuZ/FlsBaAAEAAQACowAABLmfxpbAmgAcAAEAAqMAABAqAQPwAAADAQAAAAAAAABTwKoAHAABAAKjAAAQIAEGfCVMAwEAAAAAAAAAU8A6ABwAAQACowAAECABBnwlVAMBAAAAAAAAAFPAagAcAAEAAqMAABAqAQPwAAADBQAAAAAAAABTwEoAHAABAAKjAAAQIAEGsAAOAAMAAAAAAAAAAQ==","answerfrom":"2001:dc3::35"}}},"0Y1OnyHHHeY64pWuEem8Fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"QPyAAAABAAAABAAIAWcDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWbADsAWAAIAAQACowAABAFkwBLADAABAAEAAqMAAATCACQBwFgAAQABAAKjAAAEwgAJAcAqAAEAAQACowAABMGwkBbASAABAAEAAqMAAATCkmouwAwAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAcBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875438.30163}}},"NwmO2exDjUCQobiXIEO9Vg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875444.74871,"querytime":32,"answerfrom":"2001:dc3::35","data":"5maAAAABAAAADQAPAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAAQABwBsAAgABAAKjAAAEAWjADsAbAAIAAQACowAABAFiwA7AGwACAAEAAqMAAAQBbMAOwBsAAgABAAKjAAAEAWTADsAbAAIAAQACowAABAFqwA7AGwACAAEAAqMAAAQBZ8AOwBsAAgABAAKjAAAEAW3ADsAbAAIAAQACowAABAFrwA7AGwACAAEAAqMAAALADMAbAAIAAQACowAABAFpwA7AGwACAAEAAqMAAAQBZsAOwBsAAgABAAKjAAAEAWPADsAbAAIAAQACowAABAFlwA7ADAABAAEAAqMAAATABQYewEAAAQABAAKjAAAEwCEOHsDeAAEAAQACowAABMAaXB7AYAABAAEAAqMAAATAH1AewO4AAQABAAKjAAAEwAxeHsDOAAEAAQACowAABMAjMx7AgAABAAEAAqMAAATAKl0ewDAAAQABAAKjAAAEwDZwHsC+AAEAAQACowAABMArrB7AcAABAAEAAqMAAATAME8ewKAAAQABAAKjAAAEwDSyHsBQAAEAAQACowAABMApoh7AkAABAAEAAqMAAATAN1MewAwAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMMBAABwAAQACowAAECABBQMjHQAAAAAAAAACADA="}}},"dVKTTf6INn/t4Xg/r5toXA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"NlGAAAABAAAABgAMATEBMAEwATABNAEwATABMAEwATABMAEwATABMAEwATABMgEwATABMAE4ATEBMgEyAWMBNwE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWYLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFkwGjATAACAAEAAqMAAAQBYsBowEwAAgABAAKjAAAEAWXAaMBMAAIAAQACowAABAFjwGjATAACAAEAAqMAAAQBYcBowMIAAQABAAKjAAAEx7S2NcCSAAEAAQACowAABMf9trbAsgABAAEAAqMAAATE2KkLwIIAAQABAAKjAAAEyAdWNcCiAAEAAQACowAABMt3VmXAZgABAAEAAqMAAATBAAkCwMIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CSABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAsgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwIIAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CiABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAZgAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875440.70618}}},"td2cmo+X37Q32z0SfAhp8Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.34701,"querytime":31,"answerfrom":"2001:dc3::35","data":"sVKAAAABAAAABAAIA25zMQNuaWMCZnIAABwAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZwNleHTAEMAUAAIAAQACowAABAFmwDrAFAACAAEAAqMAAAQBZcA6wCgAAQABAAKjAAAEwgAJAcBcAAEAAQACowAABMGwkBbATAABAAEAAqMAAATCkmouwDgAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAXAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8A4ABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"zQS3R7z6GBeKGH+IUB62IQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"MCOAAAABAAAADQAOCnpvbmVtYXN0ZXIDbmV0AAAGAAHAFwACAAEAAqMAABEBaAxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFkwC7AFwACAAEAAqMAAAQBbMAuwBcAAgABAAKjAAAEAWXALsAXAAIAAQACowAABAFjwC7AFwACAAEAAqMAAAQBZsAuwBcAAgABAAKjAAAEAWvALsAXAAIAAQACowAABAFiwC7AFwACAAEAAqMAAAQBacAuwBcAAgABAAKjAAAEAWfALsAXAAIAAQACowAABAFhwC7AFwACAAEAAqMAAAQBasAuwBcAAgABAAKjAAAEAW3ALsDZAAEAAQACowAABMAFBh7AqQABAAEAAqMAAATAIQ4ewHkAAQABAAKjAAAEwBpcHsBJAAEAAQACowAABMAfUB7AaQABAAEAAqMAAATADF4ewIkAAQABAAKjAAAEwCMzHsDJAAEAAQACowAABMAqXR7ALAABAAEAAqMAAATANnAewLkAAQABAAKjAAAEwCusHsDpAAEAAQACowAABMAwTx7AmQABAAEAAqMAAATANLIewFkAAQABAAKjAAAEwCmiHsD5AAEAAQACowAABMA3Ux7A2QAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875444.3995}}},"29HegVtbyPWaDm48Y6S6RQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875437.12608,"data":"Ap2AAAABAAAABAAIAmZyAAAGAAHADAACAAEAAqMAAAwBZQNleHQDbmljwAzADAACAAEAAqMAAAQBZ8AiwAwAAgABAAKjAAAEAWTAJsAMAAIAAQACowAABAFmwCLASAABAAEAAqMAAATCAAkBwCAAAQABAAKjAAAEwbCQFsBYAAEAAQACowAABMKSai7AOAABAAEAAqMAAATCACQBwEgAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcAgABwAAQACowAAECoADXgAAAECAZMBdgFEACLAWAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwDgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"2001:dc3::35"}}},"PBlOUdigoXz0ayiyPtsxqw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":33,"timestamp":1626875437.99456,"data":"TEiAAAABAAAABAAIAWYDZXh0A25pYwJmcgAAAQABwBYAAgABAAKjAAAEAWTAEsAWAAIAAQACowAABAFlwA7AFgACAAEAAqMAAAQBZ8AOwBYAAgABAAKjAAACwAzADAABAAEAAqMAAATCkmouwCoAAQABAAKjAAAEwgAJAcA6AAEAAQACowAABMGwkBbASgABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8AqABwAAQACowAAECABBngADAAAAAAAAAAAAAHAOgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"2001:dc3::35"}}},"v3bnT3mxa7enSqJowgjjtA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875439.83073,"querytime":32,"answerfrom":"2001:dc3::35","data":"yCeAAAABAAAABAAIA25zMwNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFlA2V4dMAQwBQAAgABAAKjAAAEAWbAKsAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAQBZ8AqwEwAAQABAAKjAAAEwgAJAcAoAAEAAQACowAABMGwkBbAPAABAAEAAqMAAATCkmouwFwAAQABAAKjAAAEwgAkAcBMABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwDwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BcABwAAQACowAAECABBngATAAAAAAAAAAAAAE="}}},"adP+90hFKW1Uzd20+9yl5g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875437.27049,"querytime":32,"answerfrom":"2001:dc3::35","data":"GvyAAAABAAAABAAIAWQDbmljAmZyAAABAAHAEgACAAEAAqMAAAgBZgNleHTADsASAAIAAQACowAABAFlwCjAEgACAAEAAqMAAAQBZ8AowBIAAgABAAKjAAACwAzADAABAAEAAqMAAATCAAkBwDoAAQABAAKjAAAEwbCQFsAmAAEAAQACowAABMKSai7ASgABAAEAAqMAAATCACQBwAwAHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcA6ABwAAQACowAAECoADXgAAAECAZMBdgFEACLAJgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"gNvppsjyN6Isa7C+Jawn6w":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":31,"timestamp":1626875442.33793,"data":"DgiAAAABAAAABgAMATIBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE1ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWQLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFmwGjATAACAAEAAqMAAAQBY8BowEwAAgABAAKjAAAEAWXAaMBMAAIAAQACowAABAFiwGjATAACAAEAAqMAAAQBYcBowMIAAQABAAKjAAAEx7S2NcCyAAEAAQACowAABMf9trbAkgABAAEAAqMAAATE2KkLwGYAAQABAAKjAAAEyAdWNcCiAAEAAQACowAABMt3VmXAggABAAEAAqMAAATBAAkCwMIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8CyABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAkgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwGYAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CiABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAggAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","answerfrom":"2001:dc3::35"}}},"QIgmZF9uZaI1+8Y34m3zGw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"h8yAAAABAAAABAAIAWQDbmljAmZyAAAcAAHAEgACAAEAAqMAAAgBZwNleHTADsASAAIAAQACowAABAFmwCjAEgACAAEAAqMAAAQBZcAowBIAAgABAAKjAAACwAzADAAcAAEAAqMAABAgAQZ4AAwAAAAAAAAAAAABwAwAAQABAAKjAAAEwgAJAcBKAAEAAQACowAABMGwkBbAOgABAAEAAqMAAATCkmouwCYAAQABAAKjAAAEwgAkAcBKABwAAQACowAAECoADXgAAAECAZMBdgFEACLAOgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCYAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","timestamp":1626875437.42943,"querytime":32}}},"dqdRTp15sptDjWNc+Ny8fA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875439.19769,"data":"EACAAAABAAAABAAIA25zMQNuaWMCZnIAAAEAAcAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAgBZQNleHTAEMAUAAIAAQACowAABAFmwDrAFAACAAEAAqMAAAQBZ8A6wCgAAQABAAKjAAAEwgAJAcA4AAEAAQACowAABMGwkBbATAABAAEAAqMAAATCkmouwFwAAQABAAKjAAAEwgAkAcAoABwAAQACowAAECABBngADAAAAAAAAAAAAAHAOAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BcABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35"}}},"KoOHMqlriAzfyyS3tJUVhQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875444.86204,"querytime":32,"answerfrom":"2001:dc3::35","data":"VA2AAAABAAAADQAOA2F2NAVuc3RsZANjb20AAAEAAcAWAAIAAQACowAAFAFtDGd0bGQtc2VydmVycwNuZXQAwBYAAgABAAKjAAAEAWPALcAWAAIAAQACowAABAFiwC3AFgACAAEAAqMAAAQBZcAtwBYAAgABAAKjAAAEAWzALcAWAAIAAQACowAABAFnwC3AFgACAAEAAqMAAAQBZsAtwBYAAgABAAKjAAAEAWvALcAWAAIAAQACowAABAFowC3AFgACAAEAAqMAAAQBacAtwBYAAgABAAKjAAAEAWHALcAWAAIAAQACowAABAFkwC3AFgACAAEAAqMAAAQBasAtwNsAAQABAAKjAAAEwAUGHsBbAAEAAQACowAABMAhDh7ASwABAAEAAqMAAATAGlwewOsAAQABAAKjAAAEwB9QHsBrAAEAAQACowAABMAMXh7AmwABAAEAAqMAAATAIzMewIsAAQABAAKjAAAEwCpdHsC7AAEAAQACowAABMA2cB7AywABAAEAAqMAAATAK6wewPsAAQABAAKjAAAEwDBPHsCrAAEAAQACowAABMA0sh7AewABAAEAAqMAAATAKaIewCsAAQABAAKjAAAEwDdTHsDbABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"Hx/wBzZ7dxT0btOz2sXTow":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.01031,"querytime":32,"answerfrom":"2001:dc3::35","data":"L+CAAAABAAAADQAOA2F2NAVuc3RsZANjb20AABwAAcAWAAIAAQACowAAFAFjDGd0bGQtc2VydmVycwNuZXQAwBYAAgABAAKjAAAEAWXALcAWAAIAAQACowAABAFowC3AFgACAAEAAqMAAAQBbMAtwBYAAgABAAKjAAAEAWTALcAWAAIAAQACowAABAFtwC3AFgACAAEAAqMAAAQBYsAtwBYAAgABAAKjAAAEAWHALcAWAAIAAQACowAABAFpwC3AFgACAAEAAqMAAAQBZ8AtwBYAAgABAAKjAAAEAWrALcAWAAIAAQACowAABAFmwC3AFgACAAEAAqMAAAQBa8AtwKsAAQABAAKjAAAEwAUGHsCbAAEAAQACowAABMAhDh7AKwABAAEAAqMAAATAGlwewHsAAQABAAKjAAAEwB9QHsBLAAEAAQACowAABMAMXh7A6wABAAEAAqMAAATAIzMewMsAAQABAAKjAAAEwCpdHsBbAAEAAQACowAABMA2cB7AuwABAAEAAqMAAATAK6wewNsAAQABAAKjAAAEwDBPHsD7AAEAAQACowAABMA0sh7AawABAAEAAqMAAATAKaIewIsAAQABAAKjAAAEwDdTHsCrABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"8QJIMKoYjfeWdcOUu1cfeg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":33,"timestamp":1626875438.44742,"data":"S2KAAAABAAAABAAIAWcDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWbADsAWAAIAAQACowAABAFkwBLADAAcAAEAAqMAABAgAQZ4AEwAAAAAAAAAAAABwAwAAQABAAKjAAAEwgAkAcBYAAEAAQACowAABMIACQHAKgABAAEAAqMAAATBsJAWwEgAAQABAAKjAAAEwpJqLsBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwEgAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAUw==","answerfrom":"2001:dc3::35"}}},"fxPbDm0VpYQV73LLw/20YA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.99573,"querytime":34,"answerfrom":"2001:dc3::35","data":"EFeAAAABAAAADQAOA25zMQlhc25sb29rdXAKem9uZW1hc3RlcgNuZXQAABwAAcAlAAIAAQACowAAEQFmDGd0bGQtc2VydmVyc8AlwCUAAgABAAKjAAAEAWPAPMAlAAIAAQACowAABAFpwDzAJQACAAEAAqMAAAQBZcA8wCUAAgABAAKjAAAEAWrAPMAlAAIAAQACowAABAFiwDzAJQACAAEAAqMAAAQBYcA8wCUAAgABAAKjAAAEAWfAPMAlAAIAAQACowAABAFswDzAJQACAAEAAqMAAAQBa8A8wCUAAgABAAKjAAAEAWTAPMAlAAIAAQACowAABAFowDzAJQACAAEAAqMAAAQBbcA8wKcAAQABAAKjAAAEwAUGHsCXAAEAAQACowAABMAhDh7AVwABAAEAAqMAAATAGlwewOcAAQABAAKjAAAEwB9QHsB3AAEAAQACowAABMAMXh7AOgABAAEAAqMAAATAIzMewLcAAQABAAKjAAAEwCpdHsD3AAEAAQACowAABMA2cB7AZwABAAEAAqMAAATAK6wewIcAAQABAAKjAAAEwDBPHsDXAAEAAQACowAABMA0sh7AxwABAAEAAqMAAATAKaIewQcAAQABAAKjAAAEwDdTHsCnABwAAQACowAAECABBQOoPgAAAAAAAAACADA="}}},"Lm8PThfvr4TSG5Oo/xY+Jw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"Q8KAAAABAAAABAAICWRuc21hc3RlcgNuaWMCZnIAABwAAcAaAAIAAQACowAACAFlA2V4dMAWwBoAAgABAAKjAAAEAWfAMMAaAAIAAQACowAABAFkwBbAGgACAAEAAqMAAAQBZsAwwFIAAQABAAKjAAAEwgAJAcAuAAEAAQACowAABMGwkBbAYgABAAEAAqMAAATCkmouwEIAAQABAAKjAAAEwgAkAcBSABwAAQACowAAECABBngADAAAAAAAAAAAAAHALgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BCABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875456.12675}}},"cSyobwmK5IBS1MITWTINfw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"G8GAAAABAAAADQAOAXoEYXJpbgNuZXQAABwAAcATAAIAAQACowAAEQFsDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWvAKsATAAIAAQACowAABAFlwCrAEwACAAEAAqMAAAQBacAqwBMAAgABAAKjAAAEAWbAKsATAAIAAQACowAABAFowCrAEwACAAEAAqMAAAQBasAqwBMAAgABAAKjAAAEAWfAKsATAAIAAQACowAABAFhwCrAEwACAAEAAqMAAAQBbcAqwBMAAgABAAKjAAAEAWLAKsATAAIAAQACowAABAFjwCrAEwACAAEAAqMAAAQBZMAqwLUAAQABAAKjAAAEwAUGHsDVAAEAAQACowAABMAhDh7A5QABAAEAAqMAAATAGlwewPUAAQABAAKjAAAEwB9QHsBVAAEAAQACowAABMAMXh7AdQABAAEAAqMAAATAIzMewKUAAQABAAKjAAAEwCpdHsCFAAEAAQACowAABMA2cB7AZQABAAEAAqMAAATAK6wewJUAAQABAAKjAAAEwDBPHsBFAAEAAQACowAABMA0sh7AKAABAAEAAqMAAATAKaIewMUAAQABAAKjAAAEwDdTHsC1ABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1626875441.59912,"querytime":32}}},"4YeoSYuQtm/OKn1ycfuP2Q":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"K+6AAAABAAAABAAIBWFmbmljAmZyAAAGAAHAEgACAAEAAqMAAAwBZwNleHQDbmljwBLAEgACAAEAAqMAAAQBZsAowBIAAgABAAKjAAAEAWXAKMASAAIAAQACowAABAFkwCzAXgABAAEAAqMAAATCAAkBwE4AAQABAAKjAAAEwbCQFsA+AAEAAQACowAABMKSai7AJgABAAEAAqMAAATCACQBwF4AHAABAAKjAAAQIAEGeAAMAAAAAAAAAAAAAcBOABwAAQACowAAECoADXgAAAECAZMBdgFEACLAPgAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwCYAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875436.96396}}},"f39y8o9yMy+2CJ3Tild/Hw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"05OAAAABAAAABAAIA25zMgNuaWMCZnIAABwAAcAUAAIAAQACowAACAFnA2V4dMAQwBQAAgABAAKjAAAEAWXAKsAUAAIAAQACowAABAFkwBDAFAACAAEAAqMAAAQBZsAqwEwAAQABAAKjAAAEwgAJAcA8AAEAAQACowAABMGwkBbAXAABAAEAAqMAAATCkmouwCgAAQABAAKjAAAEwgAkAcBMABwAAQACowAAECABBngADAAAAAAAAAAAAAHAPAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwFwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8AoABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875439.66045}}},"kPMzuIR4sho5B5qTORsFWg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875437.83643,"querytime":31,"answerfrom":"2001:dc3::35","data":"0TCAAAABAAAABAAIAWUDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAACwAzAFgACAAEAAqMAAAQBZMASwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAABAFmwA7ADAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwAwAAQABAAKjAAAEwbCQFsA4AAEAAQACowAABMIACQHAWAABAAEAAqMAAATCkmouwEgAAQABAAKjAAAEwgAkAcA4ABwAAQACowAAECABBngADAAAAAAAAAAAAAHAWAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwEgAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ=="}}},"xA39FCnoMsmugxaD74lRSw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"DoeAAAABAAAABgAMATEBNAMxMzQDMTkyB2luLWFkZHIEYXJwYQAADAABwBgAAgABAAKjAAAUAWQPaW4tYWRkci1zZXJ2ZXJzwCDAGAACAAEAAqMAAAQBYcA4wBgAAgABAAKjAAAEAWXAOMAYAAIAAQACowAABAFjwDjAGAACAAEAAqMAAAQBZsA4wBgAAgABAAKjAAAEAWLAOMBWAAEAAQACowAABMe0tjXAlgABAAEAAqMAAATH/be3wHYAAQABAAKjAAAExNipCsA2AAEAAQACowAABMgKPDXAZgABAAEAAqMAAATLd1ZlwIYAAQABAAKjAAAEwQAJAcBWABwAAQACowAAECYgADfgAAAAAAAAAAAAAFPAlgAcAAEAAqMAABAgAQUAAIcAAAAAAAAAAACHwHYAHAABAAKjAAAQIAFD+AEQAAAAAAAAAAAAEMA2ABwAAQACowAAECABE8dwEAAAAAAAAAAAAFPAZgAcAAEAAqMAABAgAQ3YAAYAAAAAAAAAAAEBwIYAHAABAAKjAAAQIAEGfADgAAAAAAAAAAAAAQ==","timestamp":1626875441.35355,"querytime":32}}},"Y1ZvIYRyV6Gaf0yziEjgFw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"U/SAAAABAAAACgAPA25zMwNuaWMCc2UAABwAAcAUAAIAAQACowAABwFnAm5zwBTAFAACAAEAAqMAAAQBbcAqwBQAAgABAAKjAAAEAWbAKsAUAAIAAQACowAABAFiwCrAFAACAAEAAqMAAAQBYcAqwBQAAgABAAKjAAAEAXrAKsAUAAIAAQACowAABAF5wCrAFAACAAEAAqMAAAQBacAqwBQAAgABAAKjAAAEAWPAKsAUAAIAAQACowAABAF4wCrAawABAAEAAqMAAATAJJBrwFsAAQABAAKjAAAEwCSFa8CrAAEAAQACowAABMAkh2vASwABAAEAAqMAAATARzU1wCgAAQABAAKjAAAEgu8FcsCbAAEAAQACowAABMKSahbAOwABAAEAAqMAAATCAAtwwLsAAQABAAKjAAAE1WwZBMCLAAEAAQACowAABLmfxZbAewABAAEAAqMAAAS5n8aWwGsAHAABAAKjAAAQKgED8AAAAwEAAAAAAAAAU8BbABwAAQACowAAECABBnwlTAMBAAAAAAAAAFPAqwAcAAEAAqMAABAgAQZ8JVQDAQAAAAAAAABTwEsAHAABAAKjAAAQKgED8AAAAwUAAAAAAAAAU8AoABwAAQACowAAECABBrAADgADAAAAAAAAAAE=","timestamp":1626875444.29422,"querytime":32}}},"OMEA0Jt12vdHI/DIOjRKdA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"timestamp":1626875445.15324,"querytime":32,"answerfrom":"2001:dc3::35","data":"zBuAAAABAAAADQAPAWEMZ3RsZC1zZXJ2ZXJzA25ldAAAHAABwBsAAgABAAKjAAAEAWbADsAbAAIAAQACowAABAFqwA7AGwACAAEAAqMAAAQBZ8AOwBsAAgABAAKjAAAEAWXADsAbAAIAAQACowAAAsAMwBsAAgABAAKjAAAEAWTADsAbAAIAAQACowAABAFjwA7AGwACAAEAAqMAAAQBYsAOwBsAAgABAAKjAAAEAWnADsAbAAIAAQACowAABAFowA7AGwACAAEAAqMAAAQBbcAOwBsAAgABAAKjAAAEAWzADsAbAAIAAQACowAABAFrwA7ADAAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAwwAwAAQABAAKjAAAEwAUGHsCeAAEAAQACowAABMAhDh7AjgABAAEAAqMAAATAGlwewH4AAQABAAKjAAAEwB9QHsBgAAEAAQACowAABMAMXh7AMAABAAEAAqMAAATAIzMewFAAAQABAAKjAAAEwCpdHsC+AAEAAQACowAABMA2cB7ArgABAAEAAqMAAATAK6wewEAAAQABAAKjAAAEwDBPHsDuAAEAAQACowAABMA0sh7A3gABAAEAAqMAAATAKaIewM4AAQABAAKjAAAEwDdTHsCeABwAAQACowAAECABBQMjHQAAAAAAAAACADA="}}},"nLqvsXvTOBR/25TgODnu7g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875444.07242,"data":"f9yAAAABAAAADQAOCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAABgABwCEAAgABAAKjAAARAWcMZ3RsZC1zZXJ2ZXJzwCHAIQACAAEAAqMAAAQBYsA4wCEAAgABAAKjAAAEAWPAOMAhAAIAAQACowAABAFowDjAIQACAAEAAqMAAAQBasA4wCEAAgABAAKjAAAEAWHAOMAhAAIAAQACowAABAFtwDjAIQACAAEAAqMAAAQBacA4wCEAAgABAAKjAAAEAWXAOMAhAAIAAQACowAABAFkwDjAIQACAAEAAqMAAAQBbMA4wCEAAgABAAKjAAAEAWvAOMAhAAIAAQACowAABAFmwDjAkwABAAEAAqMAAATABQYewFMAAQABAAKjAAAEwCEOHsBjAAEAAQACowAABMAaXB7A0wABAAEAAqMAAATAH1AewMMAAQABAAKjAAAEwAxeHsEDAAEAAQACowAABMAjMx7ANgABAAEAAqMAAATAKl0ewHMAAQABAAKjAAAEwDZwHsCzAAEAAQACowAABMArrB7AgwABAAEAAqMAAATAME8ewPMAAQABAAKjAAAEwDSyHsDjAAEAAQACowAABMApoh7AowABAAEAAqMAAATAN1MewJMAHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMA==","answerfrom":"2001:dc3::35"}}},"UBMcusig6w3synYwbp7omA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"c9OAAAABAAAADQAOBXJpcm5zBGFyaW4DbmV0AAABAAHAFwACAAEAAqMAABEBYwxndGxkLXNlcnZlcnPAF8AXAAIAAQACowAABAFowC7AFwACAAEAAqMAAAQBYcAuwBcAAgABAAKjAAAEAWLALsAXAAIAAQACowAABAFrwC7AFwACAAEAAqMAAAQBZcAuwBcAAgABAAKjAAAEAWnALsAXAAIAAQACowAABAFmwC7AFwACAAEAAqMAAAQBZMAuwBcAAgABAAKjAAAEAWzALsAXAAIAAQACowAABAFqwC7AFwACAAEAAqMAAAQBbcAuwBcAAgABAAKjAAAEAWfALsBZAAEAAQACowAABMAFBh7AaQABAAEAAqMAAATAIQ4ewCwAAQABAAKjAAAEwBpcHsC5AAEAAQACowAABMAfUB7AiQABAAEAAqMAAATADF4ewKkAAQABAAKjAAAEwCMzHsD5AAEAAQACowAABMAqXR7ASQABAAEAAqMAAATANnAewJkAAQABAAKjAAAEwCusHsDZAAEAAQACowAABMAwTx7AeQABAAEAAqMAAATANLIewMkAAQABAAKjAAAEwCmiHsDpAAEAAQACowAABMA3Ux7AWQAcAAEAAqMAABAgAQUDqD4AAAAAAAAAAgAw","answerfrom":"2001:dc3::35","querytime":32,"timestamp":1626875440.79812}}},"SwbApgSh9mA6B3tXP/GkUg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"TWOAAAABAAAABAAIA25zMgNuaWMCZnIAAAEAAcAUAAIAAQACowAACAFlA2V4dMAQwBQAAgABAAKjAAAEAWTAEMAUAAIAAQACowAABAFnwCrAFAACAAEAAqMAAAQBZsAqwDwAAQABAAKjAAAEwgAJAcAoAAEAAQACowAABMGwkBbAXAABAAEAAqMAAATCkmouwEwAAQABAAKjAAAEwgAkAcA8ABwAAQACowAAECABBngADAAAAAAAAAAAAAHAKAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwFwAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BMABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","timestamp":1626875439.50334,"querytime":32}}},"RE9uT98EtnHNKxECV54OHw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":33,"timestamp":1626875455.97054,"data":"d8iAAAABAAAABAAICWRuc21hc3RlcgNuaWMCZnIAAAEAAcAaAAIAAQACowAACAFlA2V4dMAWwBoAAgABAAKjAAAEAWTAFsAaAAIAAQACowAABAFnwDDAGgACAAEAAqMAAAQBZsAwwEIAAQABAAKjAAAEwgAJAcAuAAEAAQACowAABMGwkBbAYgABAAEAAqMAAATCkmouwFIAAQABAAKjAAAEwgAkAcBCABwAAQACowAAECABBngADAAAAAAAAAAAAAHALgAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwGIAHAABAAKjAAAQIAEGfBAQABEAAAAAAAAAU8BSABwAAQACowAAECABBngATAAAAAAAAAAAAAE=","answerfrom":"2001:dc3::35"}}},"sNttT2q5QOS0nfEEkjvGLQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875444.58521,"data":"YD6AAAABAAAADQAPA25ldAAABgABwAwAAgABAAKjAAARAWIMZ3RsZC1zZXJ2ZXJzwAzADAACAAEAAqMAAAQBY8AjwAwAAgABAAKjAAAEAWjAI8AMAAIAAQACowAABAFrwCPADAACAAEAAqMAAAQBZsAjwAwAAgABAAKjAAAEAWfAI8AMAAIAAQACowAABAFqwCPADAACAAEAAqMAAAQBbMAjwAwAAgABAAKjAAAEAW3AI8AMAAIAAQACowAABAFkwCPADAACAAEAAqMAAAQBZcAjwAwAAgABAAKjAAAEAWHAI8AMAAIAAQACowAABAFpwCPA3gABAAEAAqMAAATABQYewCEAAQABAAKjAAAEwCEOHsA+AAEAAQACowAABMAaXB7AvgABAAEAAqMAAATAH1AewM4AAQABAAKjAAAEwAxeHsBuAAEAAQACowAABMAjMx7AfgABAAEAAqMAAATAKl0ewE4AAQABAAKjAAAEwDZwHsDuAAEAAQACowAABMArrB7AjgABAAEAAqMAAATAME8ewF4AAQABAAKjAAAEwDSyHsCeAAEAAQACowAABMApoh7ArgABAAEAAqMAAATAN1MewN4AHAABAAKjAAAQIAEFA6g+AAAAAAAAAAIAMMAhABwAAQACowAAECABBQMjHQAAAAAAAAACADA=","answerfrom":"2001:dc3::35"}}},"fMgGdfwEQk2v5buwJaqqRA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"5yqAAAABAAAACgAPA25zMwNuaWMCc2UAAAEAAcAUAAIAAQACowAABwFhAm5zwBTAFAACAAEAAqMAAAQBZsAqwBQAAgABAAKjAAAEAWfAKsAUAAIAAQACowAABAFjwCrAFAACAAEAAqMAAAQBacAqwBQAAgABAAKjAAAEAW3AKsAUAAIAAQACowAABAF6wCrAFAACAAEAAqMAAAQBYsAqwBQAAgABAAKjAAAEAXnAKsAUAAIAAQACowAABAF4wCrAKAABAAEAAqMAAATAJJBrwJsAAQABAAKjAAAEwCSFa8BbAAEAAQACowAABMAkh2vAOwABAAEAAqMAAATARzU1wEsAAQABAAKjAAAEgu8FcsBrAAEAAQACowAABMKSahbAewABAAEAAqMAAATCAAtwwLsAAQABAAKjAAAE1WwZBMCrAAEAAQACowAABLmfxZbAiwABAAEAAqMAAAS5n8aWwCgAHAABAAKjAAAQKgED8AAAAwEAAAAAAAAAU8CbABwAAQACowAAECABBnwlTAMBAAAAAAAAAFPAWwAcAAEAAqMAABAgAQZ8JVQDAQAAAAAAAABTwDsAHAABAAKjAAAQKgED8AAAAwUAAAAAAAAAU8BLABwAAQACowAAECABBrAADgADAAAAAAAAAAE=","answerfrom":"2001:dc3::35","querytime":34,"timestamp":1626875444.18366}}},"txj88I7p+6ryfLcyf3NH/g":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"HVGAAAABAAAABgAMATQBMAI5MwMxOTIHaW4tYWRkcgRhcnBhAAAMAAHAFwACAAEAAqMAABQBZA9pbi1hZGRyLXNlcnZlcnPAH8AXAAIAAQACowAABAFmwDfAFwACAAEAAqMAAAQBYsA3wBcAAgABAAKjAAAEAWXAN8AXAAIAAQACowAABAFjwDfAFwACAAEAAqMAAAQBYcA3wJUAAQABAAKjAAAEx7S2NcBlAAEAAQACowAABMf9t7fAhQABAAEAAqMAAATE2KkKwDUAAQABAAKjAAAEyAo8NcB1AAEAAQACowAABMt3VmXAVQABAAEAAqMAAATBAAkBwJUAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8BlABwAAQACowAAECABBQAAhwAAAAAAAAAAAIfAhQAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAAQwDUAHAABAAKjAAAQIAETx3AQAAAAAAAAAAAAU8B1ABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAVQAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAB","answerfrom":"2001:dc3::35","querytime":33,"timestamp":1626875441.98624}}},"VZzWsSoApJvJHihc4YAsIQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:dc3::35","data":"Y4qAAAABAAAADQAOAXoEYXJpbgNuZXQAAAEAAcATAAIAAQACowAAEQFoDGd0bGQtc2VydmVyc8ATwBMAAgABAAKjAAAEAWXAKsATAAIAAQACowAABAFrwCrAEwACAAEAAqMAAAQBasAqwBMAAgABAAKjAAAEAWnAKsATAAIAAQACowAABAFtwCrAEwACAAEAAqMAAAQBbMAqwBMAAgABAAKjAAAEAWfAKsATAAIAAQACowAABAFiwCrAEwACAAEAAqMAAAQBZsAqwBMAAgABAAKjAAAEAWHAKsATAAIAAQACowAABAFjwCrAEwACAAEAAqMAAAQBZMAqwNUAAQABAAKjAAAEwAUGHsC1AAEAAQACowAABMAhDh7A5QABAAEAAqMAAATAGlwewPUAAQABAAKjAAAEwB9QHsBFAAEAAQACowAABMAMXh7AxQABAAEAAqMAAATAIzMewKUAAQABAAKjAAAEwCpdHsAoAAEAAQACowAABMA2cB7AdQABAAEAAqMAAATAK6wewGUAAQABAAKjAAAEwDBPHsBVAAEAAQACowAABMA0sh7AlQABAAEAAqMAAATAKaIewIUAAQABAAKjAAAEwDdTHsDVABwAAQACowAAECABBQOoPgAAAAAAAAACADA=","timestamp":1626875441.4483,"querytime":32}}},"MRObrmyvWJVMundftQXlfg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875445.58351,"data":"86KAAAABAAAACgAPAm5zA25pYwJzZQAAAQABwBMAAgABAAKjAAAHAWECbnPAE8ATAAIAAQACowAABAFmwCnAEwACAAEAAqMAAAQBesApwBMAAgABAAKjAAAEAWnAKcATAAIAAQACowAABAF5wCnAEwACAAEAAqMAAAQBY8ApwBMAAgABAAKjAAAEAW3AKcATAAIAAQACowAABAFiwCnAEwACAAEAAqMAAAQBZ8ApwBMAAgABAAKjAAAEAXjAKcAnAAEAAQACowAABMAkkGvAmgABAAEAAqMAAATAJIVrwHoAAQABAAKjAAAEwCSHa8A6AAEAAQACowAABMBHNTXAqgABAAEAAqMAAASC7wVywFoAAQABAAKjAAAEwpJqFsCKAAEAAQACowAABMIAC3DAugABAAEAAqMAAATVbBkEwGoAAQABAAKjAAAEuZ/FlsBKAAEAAQACowAABLmfxpbAJwAcAAEAAqMAABAqAQPwAAADAQAAAAAAAABTwJoAHAABAAKjAAAQIAEGfCVMAwEAAAAAAAAAU8B6ABwAAQACowAAECABBnwlVAMBAAAAAAAAAFPAOgAcAAEAAqMAABAqAQPwAAADBQAAAAAAAABTwKoAHAABAAKjAAAQIAEGsAAOAAMAAAAAAAAAAQ==","answerfrom":"2001:dc3::35"}}},"DopGLtTlD8qdrSAvQzI7OA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"c6CAAAABAAAABgAMATEBMAEwATABMQEwATABMAEwATABMAEwATABMAEwATABMQEwATABMAE2ATABMAEzATABNgE2ATABMQEwATABMgNpcDYEYXJwYQAADAABwEwAAgABAAKjAAAQAWQLaXA2LXNlcnZlcnPAUMBMAAIAAQACowAABAFmwGjATAACAAEAAqMAAAQBZcBowEwAAgABAAKjAAAEAWHAaMBMAAIAAQACowAABAFjwGjATAACAAEAAqMAAAQBYsBowKIAAQABAAKjAAAEx7S2NcDCAAEAAQACowAABMf9trbAsgABAAEAAqMAAATE2KkLwGYAAQABAAKjAAAEyAdWNcCSAAEAAQACowAABMt3VmXAggABAAEAAqMAAATBAAkCwKIAHAABAAKjAAAQJiAAN+AAAAAAAAAAAAAAU8DCABwAAQACowAAECABBQAAhgAAAAAAAAAAAIbAsgAcAAEAAqMAABAgAUP4ARAAAAAAAAAAAAARwGYAHAABAAKjAAAQIAETx3ASAAAAAAAAAAAAU8CSABwAAQACowAAECABDdgABgAAAAAAAAAAAQHAggAcAAEAAqMAABAgAQZ8AOAAAAAAAAAAAAAC","answerfrom":"2001:dc3::35","querytime":31,"timestamp":1626875443.05415}}},"SPBaDls0FbzNIjP4Lr4aZg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":32,"timestamp":1626875438.1401,"data":"zwKAAAABAAAABAAIAWYDZXh0A25pYwJmcgAAHAABwBYAAgABAAKjAAAEAWfADsAWAAIAAQACowAAAsAMwBYAAgABAAKjAAAEAWXADsAWAAIAAQACowAABAFkwBLADAAcAAEAAqMAABAgAQZ8EBAAEQAAAAAAAABTwAwAAQABAAKjAAAEwpJqLsBYAAEAAQACowAABMIACQHASAABAAEAAqMAAATBsJAWwCoAAQABAAKjAAAEwgAkAcBYABwAAQACowAAECABBngADAAAAAAAAAAAAAHASAAcAAEAAqMAABAqAA14AAABAgGTAXYBRAAiwCoAHAABAAKjAAAQIAEGeABMAAAAAAAAAAAAAQ==","answerfrom":"2001:dc3::35"}}}} +i.root-servers.net 192.36.148.17 {} +i.root-servers.net 2001:07fe:0000:0000:0000:0000:0000:0053 {} +ns2.arin.net 2001:0500:0031:0000:0000:0000:0000:0108 {} +ns2.arin.net 199.71.0.108 {} +c.in-addr-servers.arpa 196.216.169.10 {} +c.in-addr-servers.arpa 2001:43f8:0110:0000:0000:0000:0000:0010 {} +av3.nstld.com 2001:0500:0126:0000:0000:0000:0000:0030 {} +av3.nstld.com 192.82.133.30 {} j.root-servers.net 2001:0503:0c27:0000:0000:0000:0002:0030 {} j.root-servers.net 192.58.128.30 {} -g.root-servers.net 192.112.36.4 {} -google-public-dns-a.google.com 8.8.8.8 {"+NAI2Wz9QDzqq2P7UybQeQ":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"data":"5QGBgAABAAEAAAAAA214NANuaWMCZnIAAAEAAcAMAAEAAQAAUR0ABMCGBAw=","answerfrom":"8.8.8.8","querytime":8,"timestamp":1552922488.05282}}},"F8o4iqNL5YNIxp/229fl4A":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"8.8.8.8","querytime":23,"timestamp":1552922488.00205,"data":"Nn+BgAABAAIAAAAAA25pYwJmcgAADwABwAwADwABAAAL7AAIAAkDbXg1wAzADAAPAAEAAAvsAAgAFANteDTADA=="}}},"0XkzvLCnO8Y7IyxM5lLiyw":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"8.8.8.8","querytime":8,"timestamp":1552922488.03435,"data":"1bGBgAABAAEAAAAAA214NQNuaWMCZnIAAAEAAcAMAAEAAQAAFVoABMCGBA0="}}}} -e.root-servers.net 192.203.230.10 {} -c.in-addr-servers.arpa 2001:43f8:0110:0000:0000:0000:0000:0010 {} -c.in-addr-servers.arpa 196.216.169.10 {} -l.gtld-servers.net 192.41.162.30 {} -a.ip6-servers.arpa 199.180.182.53 {} -a.ip6-servers.arpa 2620:0037:e000:0000:0000:0000:0000:0053 {} -d.in-addr-servers.arpa 2001:13c7:7010:0000:0000:0000:0000:0053 {} -d.in-addr-servers.arpa 200.10.60.53 {} -d.nic.fr 2001:0678:000c:0000:0000:0000:0000:0001 {} -d.nic.fr 194.0.9.1 {} +ns.nic.se 2001:067c:124c:100a:0000:0000:0000:0045 {"B17azTRyHl7a9Vo8oZH1fg":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"querytime":1,"timestamp":1626875445.73754,"data":"yYeEAAABAAMAAAAECnpvbmVtYXN0ZXIDbmV0AAACAAHADAACAAEAAA4QAAwDbnMyA25pYwJmcgDADAACAAEAAA4QAAsCbnMDbmljAnNlAMAMAAIAAQAADhAABgNuczPAR8BEABwAAQAADhAAECABBnwSTBAKAAAAAAAAAEXAWwAcAAEAAA4QABAgAQZ8EkwgBwAAAAAAAABFwEQAAQABAAAOEAAEW+IkLcBbAAEAAQAADhAABFviJS0=","answerfrom":"2001:67c:124c:100a::45"}}},"5BrASPi6wmwbUi8r1+EWyA":{"Zonemaster::Engine::Packet":{"Zonemaster::LDNS::Packet":{"answerfrom":"2001:67c:124c:100a::45","data":"m86AAAABAAAAAgACCWFzbmxvb2t1cAp6b25lbWFzdGVyA25ldAAAAgABwAwAAgABAAAOEAAGA25zMsAMwAwAAgABAAAOEAAGA25zMcAMwEgAAQABAAACWAAELZtgRcA2AAEAAQAAAlgABC2bYEY=","timestamp":1626875445.79889,"querytime":1}}}} +ns.nic.se 91.226.36.45 {} +av2.nstld.com 192.42.178.30 {} +av2.nstld.com 2001:0500:0125:0000:0000:0000:0000:0030 {} diff --git a/t/test01.t b/t/test01.t index a9f21b7de..375ded070 100644 --- a/t/test01.t +++ b/t/test01.t @@ -56,6 +56,9 @@ database = travis_zonemaster [SQLITE] database_file = $tempdir/zonemaster.sqlite + +[LANGUAGE] +locale = en_US EOF # Create Zonemaster::Backend::RPCAPI object @@ -75,13 +78,7 @@ if ( $db_backend eq 'SQLite' ) { # add test user is( $engine->add_api_user( { username => "zonemaster_test", api_key => "zonemaster_test's api key" } ), 1, 'API add_api_user success'); -my $user_check_query; -if ( $db_backend eq 'PostgreSQL' ) { - $user_check_query = q/SELECT * FROM users WHERE user_info->>'username' = 'zonemaster_test'/; -} -elsif ( $db_backend eq 'MySQL' || $db_backend eq 'SQLite' ) { - $user_check_query = q/SELECT * FROM users WHERE username = 'zonemaster_test'/; -} +my $user_check_query = q/SELECT * FROM users WHERE username = 'zonemaster_test'/; is( scalar( $engine->{db}->dbh->selectrow_array( $user_check_query ) ), 1 ,'API add_api_user user created' ); # add a new test to the db @@ -135,12 +132,6 @@ sub run_zonemaster_test_with_backend_API { ok( defined $test_results->{creation_time}, 'TEST1 $test_results->{creation_time} defined' ); ok( defined $test_results->{results}, 'TEST1 $test_results->{results} defined' ); cmp_ok( scalar( @{ $test_results->{results} } ), '>', 1, 'TEST1 got some results' ); - - dies_ok { $engine->get_test_results( { id => $hash_id, language => 'fr-FR' } ); } - 'API get_test_results -> [results] parameter not present (wrong language tag: underscore not hyphen)'; # Should be underscore, not hyphen. - - dies_ok { $engine->get_test_results( { id => $hash_id, language => 'zz' } ); } - 'API get_test_results -> [results] parameter not present (wrong language tag: "zz" unknown)'; # "zz" is not our configuration file. } run_zonemaster_test_with_backend_API(1); @@ -157,6 +148,124 @@ is( scalar( @$test_history ), 2, 'Two tests created' ); is( length($test_history->[0]->{id}), 16, 'Test 0 has 16 characters length hash ID' ); is( length($test_history->[1]->{id}), 16, 'Test 1 has 16 characters length hash ID' ); +subtest 'mock another client' => sub { + $frontend_params_1->{client_id} = 'Another Client'; + $frontend_params_1->{client_version} = '0.1'; + + my $hash_id = $engine->start_domain_test( $frontend_params_1 ); + ok( $hash_id, "API start_domain_test OK" ); + is( length($hash_id), 16, "Test has a 16 characters length hash ID (hash_id=$hash_id)" ); + + # check that we reuse one of the previous test + subtest 'check that previous test was reused' => sub { + my %ids = map { $_->{id} => 1 } @$test_history; + ok ( exists( $ids{$hash_id} ), "Has the same hash than previous test" ); + }; + + subtest 'check test_params values' => sub { + my $res = $engine->get_test_params( { test_id => "$hash_id" } ); + my @keys_res = sort( keys %$res ); + my @keys_params = sort( keys %$frontend_params_1 ); + + is_deeply( \@keys_res, \@keys_params, "All keys are in database" ); + + foreach my $key (@keys_res) { + if ( $key eq "client_id" or $key eq "client_version" ) { + isnt( $frontend_params_1->{$key}, $res->{$key}, "but value for key '$key' is different (which is fine)" ); + } + else { + is_deeply( $frontend_params_1->{$key}, $res->{$key}, "same value for key '$key'" ); + } + } + }; + #diag "...but values for client_id and client_version are different (which is fine)"; + +}; + +subtest 'check historic tests' => sub { + # Verifies that delegated and undelegated tests are coded correctly when started + # and that the filter option in "get_test_history" works correctly + use_ok( 'Zonemaster::Backend::TestAgent' ); + my $agent = Zonemaster::Backend::TestAgent->new( { dbtype => "$db_backend", config => $config } ); + isa_ok($agent, 'Zonemaster::Backend::TestAgent', 'agent'); + + my $domain = 'xa'; + # Non-batch for "start_domain_test": + my $params_un1 = { # undelegated, non-batch + domain => $domain, + nameservers => [ + { ns => 'ns2.nic.fr', ip => '192.134.4.1' }, + ], + }; + my $params_un2 = { # undelegated, non-batch + domain => $domain, + ds_info => [ + { keytag => 11627, algorithm => 8, digtype => 2, digest => 'a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448' }, + ], + }; + my $params_dn1 = { # delegated, non-batch + domain => $domain, + }; + # Batch for "add_batch_job" + my $domain2 = 'xb'; + my $params_ub1 = { # undelegated, batch + domains => [ $domain, $domain2 ], + test_params => { + nameservers => [ + { ns => 'ns2.nic.fr', ip => '192.134.4.1' }, + ], + }, + }; + my $params_ub2 = { # undelegated, batch + domains => [ $domain, $domain2 ], + test_params => { + ds_info => [ + { keytag => 11627, algorithm => 8, digtype => 2, digest => 'a6cca9e6027ecc80ba0f6d747923127f1d69005fe4f0ec0461bd633482595448' }, + ], + }, + }; + my $params_db1 = { # delegated, batch + domains => [ $domain, $domain2 ], + }; + # The batch jobs, $params_ub1, $params_ub2 and $params_db1, cannot be run from here due to limitation in the API. See issue #827. + + foreach my $param ($params_un1, $params_un2, $params_dn1) { + my $testid = $engine->start_domain_test( $param ); + ok( $testid, "API start_domain_test ID OK" ); + diag "running the agent on test $testid"; + $agent->run( $testid ); + my $cnt = 0; + while ($cnt < 10 ) { + my $progress = $engine->test_progress( { test_id => $testid } ); + last if $progress == 100; + $cnt++; + diag "Sleep 10 s"; + sleep 10; + }; + is( $engine->test_progress( { test_id => $testid } ), 100 , 'API test_progress -> Test finished' ); + }; + + my $test_history_delegated = $engine->get_test_history( + { + filter => 'delegated', + frontend_params => { + domain => $domain, + } + } ); + my $test_history_undelegated = $engine->get_test_history( + { + filter => 'undelegated', + frontend_params => { + domain => $domain, + } + } ); + + # diag explain( $test_history_delegated ); + is( scalar( @$test_history_delegated ), 1, 'One delegated test created' ); + # diag explain( $test_history_undelegated ); + is( scalar( @$test_history_undelegated ), 2, 'Two undelegated tests created' ); +}; + if ( $ENV{ZONEMASTER_RECORD} ) { Zonemaster::Engine->save_cache( $datafile ); } diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 8bb2b4f13..d5e257202 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -8,32 +8,37 @@ use Test::NoWarnings; use Encode; use File::ShareDir qw[dist_file]; +use JSON::PP; +use File::Temp qw[tempdir]; +use Zonemaster::Backend::Config; +use Zonemaster::Backend::RPCAPI; -subtest 'Everything but NoWarnings' => sub { +my $tempdir = tempdir( CLEANUP => 1 ); - my $can_use_threads = eval 'use threads; 1'; +my $config = Zonemaster::Backend::Config->parse( <new( + { + dbtype => $config->DB_engine, + config => $config, + } +); + +my $start_domain_test_validate_syntax = $Zonemaster::Backend::RPCAPI::extra_validators{start_domain_test}; +my $start_domain_test_schema = $Zonemaster::Backend::RPCAPI::json_schemas{start_domain_test}; + +subtest 'Everything but NoWarnings' => sub { - # Create Zonemaster::Backend::RPCAPI object - my $config = Zonemaster::Backend::Config->load_config(); - my $engine = Zonemaster::Backend::RPCAPI->new( - { - dbtype => $config->DB_engine, - config => $config, - } - ); - isa_ok( $engine, 'Zonemaster::Backend::RPCAPI' ); + my $can_use_threads = eval 'use threads; 1'; my $frontend_params = { ipv4 => 1, @@ -46,93 +51,90 @@ subtest 'Everything but NoWarnings' => sub { ]; subtest 'domain present' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => 'afnic.fr' } ); - is( $res->{status}, 'ok' ); + is( scalar @res, 0 ); }; subtest encode_utf8( 'idn domain=[é]' ) => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => 'é' } ); - is( $res->{status}, 'ok' ) - or diag( $res->{message} ); + is( scalar @res, 0 ) + or diag( encode_json @res ); }; subtest encode_utf8( 'idn domain=[éé]' ) => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => 'éé' } ); - is( $res->{status}, 'ok' ) - or diag( $res->{message} ); + is( scalar @res, 0 ) + or diag( encode_json @res ); }; subtest '253 characters long domain without dot' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.com' } ); - is( - $res->{status}, 'ok' - ) or diag( $res->{message} ); + is( scalar @res, 0 ) + or diag( encode_json @res ); }; subtest '254 characters long domain with trailing dot' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.com.' } ); - is( - $res->{status}, 'ok' - ) or diag( $res->{message} ); + is( scalar @res, 0 ) + or diag( encode_json @res ); }; subtest '254 characters long domain without trailing dot' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.club' } ); - is( - $res->{status}, 'nok' - ) or diag( $res->{message} ); + cmp_ok( scalar @res, '>', 0 ) + or diag( encode_json @res ); }; subtest '63 characters long domain label' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => '012345678901234567890123456789012345678901234567890123456789-63.fr' } ); - is( $res->{status}, 'ok' ) - or diag( $res->{message} ); + is( scalar @res, 0 ) + or diag( encode_json @res ); }; subtest '64 characters long domain label' => sub { - my $res = $engine->validate_syntax( + my @res = $engine->$start_domain_test_validate_syntax( { %$frontend_params, domain => '012345678901234567890123456789012345678901234567890123456789--64.fr' } ); - is( $res->{status}, 'nok') - or diag( $res->{message} ); + cmp_ok( scalar @res, '>', 0 ) + or diag( encode_json @res ); }; #TEST NS @@ -141,80 +143,81 @@ subtest 'Everything but NoWarnings' => sub { # domain present? $frontend_params->{nameservers}->[0]->{ns} = 'afnic.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', 'domain present' ); + is( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, 'domain present' ); # idn $frontend_params->{nameservers}->[0]->{ns} = 'é'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[é]' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( 'idn domain=[é]' ) ) + or diag( encode_json $engine->start_domain_test_validate_syntax( $frontend_params ) ); # idn $frontend_params->{nameservers}->[0]->{ns} = 'éé'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[éé]' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( 'idn domain=[éé]' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # 253 characters long domain without dot $frontend_params->{nameservers}->[0]->{ns} = '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.com'; is( - $engine->validate_syntax( $frontend_params )->{status}, 'ok', + scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( '253 characters long domain without dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # 254 characters long domain with trailing dot $frontend_params->{nameservers}->[0]->{ns} = '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.com.'; is( - $engine->validate_syntax( $frontend_params )->{status}, 'ok', + $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( '254 characters long domain with trailing dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # 254 characters long domain without trailing $frontend_params->{nameservers}->[0]->{ns} = '123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.club'; - is( - $engine->validate_syntax( $frontend_params )->{status}, 'nok', + cmp_ok( + $engine->$start_domain_test_validate_syntax( $frontend_params ), '>', 0, encode_utf8( '254 characters long domain without trailing dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # 63 characters long domain label $frontend_params->{nameservers}->[0]->{ns} = '012345678901234567890123456789012345678901234567890123456789-63.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', - encode_utf8( '63 characters long domain label' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( + scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, + encode_utf8( '63 characters long domain label' ) + ) or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # 64 characters long domain label $frontend_params->{nameservers}->[0]->{ns} = '012345678901234567890123456789012345678901234567890123456789-64-.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', + cmp_ok( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), '>', 0, encode_utf8( '64 characters long domain label' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + or diag(encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # DELEGATED TEST delete( $frontend_params->{nameservers} ); $frontend_params->{domain} = 'afnic.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'delegated domain exists' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( 'delegated domain exists' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # IP ADDRESS FORMAT $frontend_params->{domain} = 'afnic.fr'; $frontend_params->{nameservers}->[0]->{ns} = 'ns1.nic.fr'; $frontend_params->{nameservers}->[0]->{ip} = '1.2.3.4'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid IPV4' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( 'Valid IPV4' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); $frontend_params->{nameservers}->[0]->{ip} = '1.2.3.4444'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV4' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + cmp_ok( scalar $engine->$start_domain_test_validate_syntax( $frontend_params ), '>', 0, encode_utf8( 'Invalid IPV4' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); $frontend_params->{nameservers}->[0]->{ip} = 'fe80::6ef0:49ff:fe7b:e4bb'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid IPV6' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( $engine->$start_domain_test_validate_syntax( $frontend_params ), 0, encode_utf8( 'Valid IPV6' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); $frontend_params->{nameservers}->[0]->{ip} = 'fe80::6ef0:49ff:fe7b:e4bbffffff'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV6' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + cmp_ok( $engine->$start_domain_test_validate_syntax( $frontend_params ), '>', 0, encode_utf8( 'Invalid IPV6' ) ) + or diag( encode_json $engine->$start_domain_test_validate_syntax( $frontend_params ) ); # DS $frontend_params->{domain} = 'afnic.fr'; @@ -223,21 +226,54 @@ subtest 'Everything but NoWarnings' => sub { $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + $frontend_params->{ds_info}->[0]->{digtype} = 1; + $frontend_params->{ds_info}->[0]->{keytag} = 5000; + + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 0, encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 'a'; $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid Algorithm Type' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 1, encode_utf8( 'Invalid Algorithm Type' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = '01234567890123456789012345678901234567890'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid digest length' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 1, encode_utf8( 'Invalid digest length' ) ) + or diag( encode_json$engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); - $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = 'Z123456789012345678901234567890123456789'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid digest format' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 1, encode_utf8( 'Invalid digest format' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + + $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; + $frontend_params->{ds_info}->[0]->{digtype} = -1; + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 1, encode_utf8( 'Invalid digest type' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + + $frontend_params->{ds_info}->[0]->{digtype} = 1; + $frontend_params->{ds_info}->[0]->{keytag} = 'not a int'; + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 1, encode_utf8( 'Invalid keytag' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + + $frontend_params->{ds_info}->[0]->{keytag} = 5000; + + { + local $frontend_params->{language} = "zz"; + my @res = $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ); + is( scalar @res, 1, 'Invalid language, "zz" unknown' ) or diag( explain \@res ); + } + + { + local $frontend_params->{language} = "fr-FR"; + my @res = $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ); + # Because of the multiple validation layers two messages are returned here, one for the invalid format, the other for the unknown language + is( scalar @res, 2, 'Invalid language, should be underscore not hyphen' ) or diag( explain \@res ); + } + + { + local $frontend_params->{language} = "nb_NO"; + my @res = $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ); + is( scalar @res, 0, 'Valid language' ) or diag( explain \@res ); + } }; diff --git a/t/validator.t b/t/validator.t index eed5c8b4d..8ebb0f0ed 100644 --- a/t/validator.t +++ b/t/validator.t @@ -7,6 +7,7 @@ use Test::More tests => 2; use Test::NoWarnings; use Test::Differences; use Scalar::Util qw( tainted ); +use JSON::Validator::Schema::Draft7; # Get a tainted copy of a string sub taint { @@ -19,12 +20,16 @@ sub taint { return substr $string . $0, length $0; } +sub compile_schema { + return JSON::Validator::Schema::Draft7->new->coerce('booleans,numbers,strings')->data(@_); +} + subtest 'Everything but NoWarnings' => sub { use_ok( 'Zonemaster::Backend::Validator', ':untaint' ); subtest 'ds_info' => sub { - my $v = Zonemaster::Backend::Validator->new->ds_info; + my $v = compile_schema Zonemaster::Backend::Validator->new->ds_info; my $ds_info_40 = { digest => '0' x 40, algorithm => 0, digtype => 0, keytag => 0 }; my $ds_info_64 = { digest => '0' x 64, algorithm => 0, digtype => 0, keytag => 0 }; eq_or_diff [ $v->validate( $ds_info_40 ) ], [], 'accept ds_info with 40-digit hash'; @@ -32,11 +37,18 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'ip_address' => sub { - my $v = Zonemaster::Backend::Validator->new->ip_address; + my $v = compile_schema Zonemaster::Backend::Validator->new->ip_address; eq_or_diff [ $v->validate( '192.168.0.2' ) ], [], 'accept: 192.168.0.2'; eq_or_diff [ $v->validate( '2001:db8::1' ) ], [], 'accept: 2001:db8::1'; }; + subtest 'untaint_abs_path' => sub { + is scalar untaint_abs_path( '/var/db/zonemaster.sqlite' ), '/var/db/zonemaster.sqlite', 'accept: /var/db/zonemaster.sqlite'; + is scalar untaint_abs_path( 'zonemaster.sqlite' ), undef, 'reject: zonemaster.sqlite'; + is scalar untaint_abs_path( './zonemaster.sqlite' ), undef, 'reject: ./zonemaster.sqlite'; + ok !tainted( untaint_abs_path( taint( 'localhost' ) ) ), 'launder taint'; + }; + subtest 'untaint_engine_type' => sub { is scalar untaint_engine_type( 'MySQL' ), 'MySQL', 'accept: MySQL'; is scalar untaint_engine_type( 'mysql' ), 'mysql', 'accept: mysql'; @@ -47,4 +59,153 @@ subtest 'Everything but NoWarnings' => sub { is scalar untaint_engine_type( 'Excel' ), undef, 'reject: Excel'; ok !tainted( untaint_engine_type( taint( 'SQLite' ) ) ), 'launder taint'; }; + + subtest 'untaint_ip_address' => sub { + is scalar untaint_ip_address( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1'; + is scalar untaint_ip_address( '192.0.2' ), undef, 'reject: 192.0.2'; + is scalar untaint_ip_address( '192' ), undef, 'reject: 192'; + is scalar untaint_ip_address( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306'; + is scalar untaint_ip_address( '2001:db8::' ), '2001:db8::', 'accept: 2001:db8::'; + is scalar untaint_ip_address( '2001:db8::/32' ), undef, 'reject: 2001:db8::/32'; + is scalar untaint_ip_address( '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff' ), '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', 'accept: 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff'; + is scalar untaint_ip_address( '2001:db8:ffff:ffff:ffff:ffff:ffff' ), undef, 'reject: 2001:db8:ffff:ffff:ffff:ffff:ffff'; + is scalar untaint_ip_address( '2001:db8::255.255.255.254' ), '2001:db8::255.255.255.254', 'accept: 2001:db8::255.255.255.254'; + is scalar untaint_ip_address( '2001:db8::255.255.255' ), undef, 'reject: 2001:db8::255.255.255'; + is scalar untaint_ip_address( '::1' ), '::1', 'accept: ::1'; + is scalar untaint_ip_address( ':::1' ), undef, 'reject: :::1'; + ok !tainted( untaint_ip_address( taint( '192.0.2.1' ) ) ), 'launder taint'; + }; + + subtest 'untaint_ldh_domain' => sub { + is scalar untaint_ldh_domain( 'localhost' ), 'localhost', 'accept: localhost'; + is scalar untaint_ldh_domain( 'example.com' ), 'example.com', 'accept: example.com'; + is scalar untaint_ldh_domain( 'example.com.' ), 'example.com.', 'accept: example.com.'; + is scalar untaint_ldh_domain( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1'; + is scalar untaint_ldh_domain( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306'; + is scalar untaint_ldh_domain( '1/26.2.0.192.in-addr.arpa' ), undef, 'reject: 1/26.2.0.192.in-addr.arpa'; + is scalar untaint_ldh_domain( '_http.example.com' ), undef, 'reject: _http.example.com'; + ok !tainted( untaint_ldh_domain( taint( 'localhost' ) ) ), 'launder taint'; + }; + + subtest 'untaint_locale_tag' => sub { + is scalar untaint_locale_tag( 'en_US' ), 'en_US', 'accept: en_US'; + is scalar untaint_locale_tag( 'en' ), undef, 'reject: en'; + is scalar untaint_locale_tag( 'English' ), undef, 'reject: English'; + ok !tainted( untaint_locale_tag( taint( 'en_US' ) ) ), 'launder taint'; + }; + + subtest 'untaint_mariadb_database' => sub { + is scalar untaint_mariadb_database( 'zonemaster' ), 'zonemaster', 'accept: zonemaster'; + is scalar untaint_mariadb_database( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER'; + is scalar untaint_mariadb_database( 'dollar$' ), 'dollar$', 'accept: dollar$'; + is scalar untaint_mariadb_database( '$dollar' ), '$dollar', 'accept: $dollar'; + is scalar untaint_mariadb_database( '0zonemaster' ), '0zonemaster', 'accept: 0zonemaster'; + is scalar untaint_mariadb_database( 'zm_backend' ), 'zm_backend', 'accept: zm_backend'; + is scalar untaint_mariadb_database( 'zm backend' ), undef, 'reject: zm backend'; + is scalar untaint_mariadb_database( 'zm-backend' ), undef, 'reject: zm-backend'; + is scalar untaint_mariadb_database( '' ), undef, 'reject empty string'; + is scalar untaint_mariadb_database( 'zönemästër' ), undef, 'reject: zönemästër'; + is scalar untaint_mariadb_database( 'a' x 65 ), undef, 'reject 65 characters'; + is scalar untaint_mariadb_database( 'a' x 64 ), 'a' x 64, 'accept 64 characters'; + ok !tainted( untaint_mariadb_database( taint( 'zonemaster' ) ) ), 'launder taint'; + }; + + subtest 'untaint_mariadb_user' => sub { + is scalar untaint_mariadb_user( 'zonemaster' ), 'zonemaster', 'accept: zonemaster'; + is scalar untaint_mariadb_user( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER'; + is scalar untaint_mariadb_user( '$dollar' ), '$dollar', 'accept: $dollar'; + is scalar untaint_mariadb_user( '0zonemaster' ), '0zonemaster', 'accept: 0zonemaster'; + is scalar untaint_mariadb_user( 'zm_backend' ), 'zm_backend', 'accept: zm_backend'; + is scalar untaint_mariadb_user( 'zm backend' ), undef, 'reject: zm backend'; + is scalar untaint_mariadb_user( 'zm-backend' ), undef, 'reject: zm-backend'; + is scalar untaint_mariadb_user( '' ), undef, 'reject empty string'; + is scalar untaint_mariadb_user( 'zönemästër' ), undef, 'reject: zönemästër'; + is scalar untaint_mariadb_user( 'a' x 81 ), undef, 'reject 81 characters'; + is scalar untaint_mariadb_user( 'a' x 80 ), 'a' x 80, 'accept 80 characters'; + ok !tainted( untaint_mariadb_user( taint( 'zonemaster' ) ) ), 'launder taint'; + }; + + subtest 'untaint_password' => sub { + is scalar untaint_password( '123456' ), '123456', 'accept: 123456'; + is scalar untaint_password( 'password' ), 'password', 'accept: password'; + is scalar untaint_password( '!@#$%^&*<' ), '!@#$%^&*<', 'accept: !@#$%^&*<'; + is scalar untaint_password( 'Qwertyuiop' ), 'Qwertyuiop', 'accept: Qwertyuiop'; + is scalar untaint_password( 'battery staple' ), 'battery staple', 'accept: battery staple'; + is scalar untaint_password( '' ), '', 'accept the empty string'; + is scalar untaint_password( "\t" ), undef, 'reject tab character'; + is scalar untaint_password( "\x80" ), undef, 'reject del character'; + is scalar untaint_password( ' x' ), undef, 'reject initial space'; + is scalar untaint_password( ' sub { + is scalar untaint_postgresql_ident( 'zonemaster' ), 'zonemaster', 'accept: zonemaster'; + is scalar untaint_postgresql_ident( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER'; + is scalar untaint_postgresql_ident( 'zm_backend' ), 'zm_backend', 'accept: zm_backend'; + is scalar untaint_postgresql_ident( 'dollar$' ), 'dollar$', 'accept: dollar$'; + is scalar untaint_postgresql_ident( '$dollar' ), undef, 'reject: $dollar'; + is scalar untaint_postgresql_ident( 'zm backend' ), undef, 'reject: zm backend'; + is scalar untaint_postgresql_ident( '0zonemaster' ), undef, 'reject: 0zonemaster'; + is scalar untaint_postgresql_ident( 'zm-backend' ), undef, 'reject: zm-backend'; + is scalar untaint_postgresql_ident( '' ), undef, 'reject empty string'; + is scalar untaint_postgresql_ident( 'zönemästër' ), undef, 'reject: zönemästër'; + is scalar untaint_postgresql_ident( 'a' x 64 ), undef, 'reject 64 characters'; + is scalar untaint_postgresql_ident( 'a' x 63 ), 'a' x 63, 'accept 63 characters'; + ok !tainted( untaint_postgresql_ident( taint( 'zonemaster' ) ) ), 'launder taint'; + }; + + subtest 'untaint_profile_name' => sub { + is scalar untaint_profile_name( 'default' ), 'default', 'accept: default'; + is scalar untaint_profile_name( '-leading-dash' ), undef, 'reject: -leading-dash'; + is scalar untaint_profile_name( 'trailing-dash-' ), undef, 'reject: trailing-dash-'; + is scalar untaint_profile_name( 'middle-dash' ), 'middle-dash', 'accept: middle-dash'; + is scalar untaint_profile_name( '_leading_underscore' ), undef, 'reject: _leading_underscore'; + is scalar untaint_profile_name( 'trailing_underscore_' ), undef, 'reject: trailing_underscore_'; + is scalar untaint_profile_name( 'middle_underscore' ), 'middle_underscore', 'accept: middle_underscore'; + is scalar untaint_profile_name( '0-leading-digit' ), '0-leading-digit', 'accept: 0-leading-digit'; + is scalar untaint_profile_name( 'a' ), 'a', 'accept: a'; + is scalar untaint_profile_name( '-' ), undef, 'reject dash'; + is scalar untaint_profile_name( '_' ), undef, 'reject underscore'; + is scalar untaint_profile_name( 'a' x 32 ), 'a' x 32, 'accept 32 characters'; + is scalar untaint_profile_name( 'a' x 33 ), undef, 'reject 33 characters'; + ok !tainted( untaint_profile_name( taint( 'default' ) ) ), 'launder taint'; + }; + + subtest 'untaint_non_negative_int' => sub { + is scalar untaint_non_negative_int( '1' ), '1', 'accept: 1'; + is scalar untaint_non_negative_int( '0' ), '0', 'accept: 0'; + is scalar untaint_non_negative_int( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_non_negative_int( '100000' ), undef, 'reject: 100000'; + is scalar untaint_non_negative_int( '0.5' ), undef, 'reject: 0.5'; + is scalar untaint_non_negative_int( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_non_negative_int( taint( '1' ) ) ), 'launder taint'; + }; + + subtest 'untaint_strictly_positive_int' => sub { + is scalar untaint_strictly_positive_int( '1' ), '1', 'accept: 1'; + is scalar untaint_strictly_positive_int( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_strictly_positive_int( '100000' ), undef, 'reject: 100000'; + is scalar untaint_strictly_positive_int( '0' ), undef, 'reject: 0'; + is scalar untaint_strictly_positive_int( '0.5' ), undef, 'reject: 0.5'; + is scalar untaint_strictly_positive_int( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_strictly_positive_int( taint( '1' ) ) ), 'launder taint'; + }; + + subtest 'untaint_strictly_positive_millis' => sub { + is scalar untaint_strictly_positive_millis( '0.5' ), '0.5', 'accept: 0.5'; + is scalar untaint_strictly_positive_millis( '0.001' ), '0.001', 'accept: 0.001'; + is scalar untaint_strictly_positive_millis( '99999.999' ), '99999.999', 'accept: 99999.999'; + is scalar untaint_strictly_positive_millis( '1' ), '1', 'accept: 1'; + is scalar untaint_strictly_positive_millis( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_strictly_positive_millis( '0.0009' ), undef, 'reject: 0.0009'; + is scalar untaint_strictly_positive_millis( '100000' ), undef, 'reject: 100000'; + is scalar untaint_strictly_positive_millis( '0' ), undef, 'reject: 0'; + is scalar untaint_strictly_positive_millis( '0.0' ), undef, 'reject: 0.0'; + is scalar untaint_strictly_positive_millis( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_strictly_positive_millis( taint( '0.5' ) ) ), 'launder taint'; + }; };