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';
+ };
};