From 8a69b9fda578c132eda53c9cdad6038dd9a07c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 21 May 2021 11:44:47 +0200 Subject: [PATCH 001/424] Implement zmb get_test_params --- script/zmb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/script/zmb b/script/zmb index 72d8e0dd3..3ee8ad40f 100755 --- a/script/zmb +++ b/script/zmb @@ -270,6 +270,34 @@ sub cmd_test_progress { } +=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, + }, + ); +} + + =head2 get_test_results zmb [GLOBAL OPTIONS] get_test_results [OPTIONS] From 34492eef03152361bcded1ee4a7d65f17393d75c Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 26 May 2021 12:55:14 +0200 Subject: [PATCH 002/424] Splits commands to make it easier to copy from Github --- docs/Installation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Installation.md b/docs/Installation.md index ba727b4f0..a2747ef58 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -624,7 +624,11 @@ To check the running daemons run: ```sh service mysql-server status # If mysql-server is installed +``` +```sh service postgresql status # If postgresql is installed +``` +```sh service zm_rpcapi status service zm_testagent status ``` From ac2811a29b66cc5c3934f67b141d79fbfef897f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 6 May 2021 12:35:43 +0200 Subject: [PATCH 003/424] Clean up --- lib/Zonemaster/Backend/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index f24fe5a9e..57a6c2a74 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -745,7 +745,7 @@ sub _normalize_engine_type { sqlite => 'SQLite', }; - return scalar $db_module_names->{ lc $value }; + return $db_module_names->{ lc $value }; } 1; From ce9ac688e3b5a1bbd7c09f64b094f8523d33daaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 10 May 2021 18:12:44 +0200 Subject: [PATCH 004/424] Add notes about incompleteness --- lib/Zonemaster/Backend/Validator.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 7150be223..1a5d9c4c1 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -22,7 +22,10 @@ our %EXPORT_TAGS = ( ], ); +# 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}$/; + +# Does not the length and number of the hex groups, nor the value ranges in the IPv4 groups 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; From dc174b3ed6f3188792044fb4969351a50eb953ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 16 Apr 2021 16:37:02 +0200 Subject: [PATCH 005/424] Validate simple property values --- docs/Configuration.md | 56 ++++- lib/Zonemaster/Backend/Config.pm | 48 ++-- lib/Zonemaster/Backend/Validator.pm | 132 +++++++++-- t/config.t | 329 ++++++++++++++++++++++++++++ t/validator.t | 112 ++++++++++ 5 files changed, 635 insertions(+), 42 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 0460bdace..fc247f6df 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -5,11 +5,14 @@ Zonemaster *Backend* is configured in `/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]. + +Each section in `backend_config.ini` is documented below. + ## DB section Available keys : `engine`, `user`, `password`, `database_name`, @@ -58,6 +61,9 @@ over this. ### polling_interval +A 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`. @@ -68,24 +74,36 @@ Available keys : `host`, `user`, `password`, `database`. ### host +An ASCII domain name. + 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. ### 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. @@ -97,24 +115,34 @@ Available keys : `host`, `user`, `password`, `database`. ### host +An ASCII domain name. + 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. ### user +A US ASCII-only [PostgreSQL identifier]. + 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]. + The name of the database to use. If this property is unspecified, the value of [DB.database_name] is used instead. @@ -126,6 +154,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. @@ -253,13 +283,15 @@ The ZONEMASTER section has several keys : ### max_zonemaster_execution_time -An integer. +A 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. +A non-negative integer. Max length 5 digits. + Number of time a test is allowed to be run again if unfinished after `max_zonemaster_execution_time`. Default value: `0`. @@ -269,7 +301,8 @@ Do not use it (keep the default value "0"), or use it with care. ### number_of_processes_for_frontend_testing -A positive integer. +A 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`. @@ -280,7 +313,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`. @@ -291,13 +325,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 positive integer. Max length 5 digits. + The shelf life of a test in seconds after its creation. Default value: `600`. @@ -312,14 +348,18 @@ Otherwise a new test request is enqueued. [DB.password]: #password [DB.user]: #user [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 +[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 diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 57a6c2a74..a181dd1fa 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -71,8 +71,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. @@ -481,22 +481,22 @@ sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMA # 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' ); + _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_positive_millis ); + _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_domain_name ); + _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_domain_name ); + _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_unsigned_int ); + _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_unsigned_int ); + _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue', \&untaint_unsigned_int ); + _create_setter( '_set_ZONEMASTER_number_of_processes_for_frontend_testing', '_ZONEMASTER_number_of_processes_for_frontend_testing', \&untaint_positive_int ); + _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing', \&untaint_positive_int ); + _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_positive_int ); } =head2 Language_Locale_hash @@ -718,14 +718,20 @@ sub new_PM { return $pm; } -# Create a setter method with a given name using the given field +# 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; }; diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 1a5d9c4c1..6d21ef682 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -7,17 +7,36 @@ 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; our @EXPORT_OK = qw( + untaint_abs_path + untaint_domain_name untaint_engine_type + untaint_mariadb_database + untaint_mariadb_user + untaint_password + untaint_positive_int + untaint_positive_millis + untaint_postgresql_ident + untaint_unsigned_int ); our %EXPORT_TAGS = ( untaint => [ qw( + untaint_abs_path + untaint_domain_name untaint_engine_type + untaint_mariadb_database + untaint_mariadb_user + untaint_password + untaint_positive_int + untaint_positive_millis + untaint_postgresql_ident + untaint_unsigned_int ) ], ); @@ -28,18 +47,43 @@ Readonly my $IPV4_RE => qr/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/; # Does not the length and number of the hex groups, nor the value ranges in the IPv4 groups 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})?$/; +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 $DOMAIN_NAME_RE1 => qr{^[a-z0-9-_/.]{1,253}[.]?$}i; +Readonly my $DOMAIN_NAME_RE2 => qr{^(?:[.]|[^.]{1,63}(?:[.][^.]{1,63})*[.]?)$}; +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 $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 9 and 6 digits in the integer and fraction components respectively +Readonly my $MILLIS_RE => qr/^(?:0|[1-9][0-9]{0,4})(?:[.][0-9]{1,3})?$/; + +# Printable ASCII but first character must not be space or '<' +Readonly my $PASSWORD_RE => qr/^(?:[\x21-\x3b\x3d-\x7e][\x20-\x7e]{0,99})?$/; + +# Allow for 15 significant digits +Readonly my $POSITIVE_INT_RE => qr/^[1-9][0-9]{0,4}$/; + +# At least one non-zero digit +Readonly my $POSITIVE_NUM_RE => qr/[^0.]/; + +# 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}$/; -Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; + +# Allow for 15 significant digits +Readonly my $UNSIGNED_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; +Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; sub joi { return JSON::Validator::Joi->new; @@ -127,6 +171,18 @@ 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 ); +} + +sub untaint_domain_name { + my ( $value ) = @_; + return _untaint_pat( $value, $DOMAIN_NAME_RE1, $DOMAIN_NAME_RE2 ); +} + =head2 untaint_engine_type Accepts the strings C<"MySQL">, C<"PostgreSQL"> and C<"SQLite">, @@ -139,14 +195,64 @@ sub untaint_engine_type { return _untaint_pat( $value , $ENGINE_TYPE_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_positive_int { + my ( $value ) = @_; + return _untaint_pat( $value, $POSITIVE_INT_RE ); +} + +sub untaint_positive_millis { + my ( $value ) = @_; + return _untaint_pat( $value, $MILLIS_RE, $POSITIVE_NUM_RE ); +} + +sub untaint_postgresql_ident { + my ( $value ) = @_; + return _untaint_pat( $value, $POSTGRESQL_IDENT_RE ); +} + +sub untaint_unsigned_int { + my ( $value ) = @_; + return _untaint_pat( $value, $UNSIGNED_INT_RE ); +} + 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/t/config.t b/t/config.t index 949459345..f786daa5d 100644 --- a/t/config.t +++ b/t/config.t @@ -201,6 +201,335 @@ 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_inteval 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 = -1 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.max_zonemaster_execution_time.*-1}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value'; + + throws_ok { + my $text = q{ + [DB] + engine = SQLite + + [ZONEMASTER] + maximal_number_of_retries = -1 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.maximal_number_of_retries.*-1}, 'die: Invalid ZONEMASTER.maximal_number_of_retries 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 = 0 + }; + Zonemaster::Backend::Config->parse( $text ); + } + qr{ZONEMASTER\.number_of_processes_for_batch_testing.*0}, '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] diff --git a/t/validator.t b/t/validator.t index eed5c8b4d..9fd511fc2 100644 --- a/t/validator.t +++ b/t/validator.t @@ -37,6 +37,22 @@ subtest 'Everything but NoWarnings' => sub { 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_domain_name' => sub { + is scalar untaint_domain_name( 'localhost' ), 'localhost', 'accept: localhost'; + is scalar untaint_domain_name( 'example.com' ), 'example.com', 'accept: example.com'; + is scalar untaint_domain_name( 'example.com.' ), 'example.com.', 'accept: example.com.'; + is scalar untaint_domain_name( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1'; + is scalar untaint_domain_name( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306'; + ok !tainted( untaint_domain_name( 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 +63,100 @@ 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_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( '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_positive_int( '1' ), '1', 'accept: 1'; + is scalar untaint_positive_int( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_positive_int( '100000' ), undef, 'reject: 100000'; + is scalar untaint_positive_int( '0' ), undef, 'reject: 0'; + is scalar untaint_positive_int( '0.5' ), undef, 'reject: 0.5'; + is scalar untaint_positive_int( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_positive_int( taint( '1' ) ) ), 'launder taint'; + }; + + subtest 'untaint_positive_millis' => sub { + is scalar untaint_positive_millis( '0.5' ), '0.5', 'accept: 0.5'; + is scalar untaint_positive_millis( '0.001' ), '0.001', 'accept: 0.001'; + is scalar untaint_positive_millis( '99999.999' ), '99999.999', 'accept: 99999.999'; + is scalar untaint_positive_millis( '1' ), '1', 'accept: 1'; + is scalar untaint_positive_millis( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_positive_millis( '0.0009' ), undef, 'reject: 0.0009'; + is scalar untaint_positive_millis( '100000' ), undef, 'reject: 100000'; + is scalar untaint_positive_millis( '0' ), undef, 'reject: 0'; + is scalar untaint_positive_millis( '0.0' ), undef, 'reject: 0.0'; + is scalar untaint_positive_millis( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_positive_millis( taint( '0.5' ) ) ), 'launder taint'; + }; + + subtest 'untaint_postgresql_ident' => 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' ), 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_unsigned_int' => sub { + is scalar untaint_unsigned_int( '1' ), '1', 'accept: 1'; + is scalar untaint_unsigned_int( '0' ), '0', 'accept: 0'; + is scalar untaint_unsigned_int( '99999' ), '99999', 'accept: 99999'; + is scalar untaint_unsigned_int( '100000' ), undef, 'reject: 100000'; + is scalar untaint_unsigned_int( '0.5' ), undef, 'reject: 0.5'; + is scalar untaint_unsigned_int( '-1' ), undef, 'reject: -1'; + ok !tainted( untaint_unsigned_int( taint( '1' ) ) ), 'launder taint'; + }; }; From afd7d109f4e9cb5483196264652640100fc41725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 12 May 2021 17:02:40 +0200 Subject: [PATCH 006/424] Add/fix some maximum length values in documentation --- docs/Configuration.md | 4 ++-- lib/Zonemaster/Backend/Validator.pm | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index fc247f6df..414140efc 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -123,7 +123,7 @@ If this property is unspecified, the value of [DB.database_host] is used instead ### user -A US ASCII-only [PostgreSQL identifier]. +A US ASCII-only [PostgreSQL identifier]. Max length 63 characters. The name of the user with sufficient permission to access the database. @@ -141,7 +141,7 @@ If this property is unspecified, the value of [DB.password] is used instead. ### database -A US ASCII-only [PostgreSQL identifier]. +A US ASCII-only [PostgreSQL identifier]. Max length 63 characters. The name of the database to use. diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 6d21ef682..b4fe0d8ea 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -63,13 +63,13 @@ Readonly my $MARIADB_DATABASE_LENGTH_RE => qr/^.{1,64}$/; Readonly my $MARIADB_IDENT_RE => qr/^[0-9a-z\$_]+$/i; Readonly my $MARIADB_USER_LENGTH_RE => qr/^.{1,80}$/u; -# Up to 9 and 6 digits in the integer and fraction components respectively +# 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})?$/; # Printable ASCII but first character must not be space or '<' Readonly my $PASSWORD_RE => qr/^(?:[\x21-\x3b\x3d-\x7e][\x20-\x7e]{0,99})?$/; -# Allow for 15 significant digits +# Allow for 5 significant digits Readonly my $POSITIVE_INT_RE => qr/^[1-9][0-9]{0,4}$/; # At least one non-zero digit @@ -81,7 +81,7 @@ Readonly my $PROFILE_NAME_RE => qr/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; -# Allow for 15 significant digits +# Allow for 5 significant digits Readonly my $UNSIGNED_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; From 6b4ae094e7f5629e2741e20cda6adbcfecedcc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 12 May 2021 17:03:14 +0200 Subject: [PATCH 007/424] Add a couple of forgotten tests --- t/validator.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/validator.t b/t/validator.t index 9fd511fc2..a994342a2 100644 --- a/t/validator.t +++ b/t/validator.t @@ -67,6 +67,7 @@ subtest 'Everything but NoWarnings' => sub { 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'; @@ -139,6 +140,7 @@ subtest 'Everything but NoWarnings' => 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'; From e152c6b48687b6b3c4a5e756c03b483855ff5265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 2 Jun 2021 14:32:44 +0200 Subject: [PATCH 008/424] Clarify allowed character sets --- docs/Configuration.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 414140efc..9da708ce5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -74,7 +74,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An ASCII domain name. +An [LDH domain name] extended with underscore (`_`) and slash (`/`). The host name of the machine on which the MySQL server is running. @@ -91,7 +91,7 @@ If this property is unspecified, the value of [DB.user] is used instead. ### password -A string of US ASCII printable characters. +A string of [US ASCII printable characters]. The first character must be neither space nor `<`. Max length 100 characters. @@ -115,7 +115,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An ASCII domain name. +An [LDH domain name] extended with underscore (`_`) and slash (`/`). The host name of the machine on which the PostgreSQL server is running. @@ -131,7 +131,7 @@ If this property is unspecified, the value of [DB.user] is used instead. ### password -A string of US ASCII printable characters. +A string of [US ASCII printable characters]. The first character must be neither space nor `<`. Max length 100 characters. @@ -353,6 +353,7 @@ Otherwise a new test request is enqueued. [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 @@ -368,6 +369,7 @@ Otherwise a new test request is enqueued. [Profile names]: 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 From 5efec29f2ece629484c7ee40c5b8453b53f8ec53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 4 Jun 2021 11:22:08 +0200 Subject: [PATCH 009/424] Restrict domain names in config to LDH --- docs/Configuration.md | 4 ++-- lib/Zonemaster/Backend/Config.pm | 4 ++-- lib/Zonemaster/Backend/Validator.pm | 12 ++++++------ t/validator.t | 16 +++++++++------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 9da708ce5..082a2f096 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -74,7 +74,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An [LDH domain name] extended with underscore (`_`) and slash (`/`). +An [LDH domain name]. The host name of the machine on which the MySQL server is running. @@ -115,7 +115,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An [LDH domain name] extended with underscore (`_`) and slash (`/`). +An [LDH domain name]. The host name of the machine on which the PostgreSQL server is running. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index a181dd1fa..9dd385eaf 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -482,11 +482,11 @@ sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMA # Compile time generation of setters for the properties documented above UNITCHECK { _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_positive_millis ); - _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_domain_name ); + _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_ldh_domain ); _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_domain_name ); + _create_setter( '_set_POSTGRESQL_host', '_POSTGRESQL_host', \&untaint_ldh_domain ); _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 ); diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index b4fe0d8ea..252711169 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -13,7 +13,7 @@ use Readonly; our @EXPORT_OK = qw( untaint_abs_path - untaint_domain_name + untaint_ldh_domain untaint_engine_type untaint_mariadb_database untaint_mariadb_user @@ -28,7 +28,7 @@ our %EXPORT_TAGS = ( untaint => [ qw( untaint_abs_path - untaint_domain_name + untaint_ldh_domain untaint_engine_type untaint_mariadb_database untaint_mariadb_user @@ -51,12 +51,12 @@ 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 $DOMAIN_NAME_RE1 => qr{^[a-z0-9-_/.]{1,253}[.]?$}i; -Readonly my $DOMAIN_NAME_RE2 => qr{^(?:[.]|[^.]{1,63}(?:[.][^.]{1,63})*[.]?)$}; 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 $MARIADB_DATABASE_LENGTH_RE => qr/^.{1,64}$/; # See: https://mariadb.com/kb/en/identifier-names/#unquoted @@ -178,9 +178,9 @@ sub untaint_abs_path { return _untaint_pred( $value, \&file_name_is_absolute ); } -sub untaint_domain_name { +sub untaint_ldh_domain { my ( $value ) = @_; - return _untaint_pat( $value, $DOMAIN_NAME_RE1, $DOMAIN_NAME_RE2 ); + return _untaint_pat( $value, $LDH_DOMAIN_RE1, $LDH_DOMAIN_RE2 ); } =head2 untaint_engine_type diff --git a/t/validator.t b/t/validator.t index a994342a2..1424bc1d4 100644 --- a/t/validator.t +++ b/t/validator.t @@ -44,13 +44,15 @@ subtest 'Everything but NoWarnings' => sub { ok !tainted( untaint_abs_path( taint( 'localhost' ) ) ), 'launder taint'; }; - subtest 'untaint_domain_name' => sub { - is scalar untaint_domain_name( 'localhost' ), 'localhost', 'accept: localhost'; - is scalar untaint_domain_name( 'example.com' ), 'example.com', 'accept: example.com'; - is scalar untaint_domain_name( 'example.com.' ), 'example.com.', 'accept: example.com.'; - is scalar untaint_domain_name( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1'; - is scalar untaint_domain_name( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306'; - ok !tainted( untaint_domain_name( taint( 'localhost' ) ) ), '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_engine_type' => sub { From bcfed7d49ab211589bcb6d3183d85e2352db575b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 8 Jun 2021 18:52:45 +0200 Subject: [PATCH 010/424] Fix typos --- lib/Zonemaster/Backend/Validator.pm | 2 +- t/config.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 252711169..c7dd1b01d 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -44,7 +44,7 @@ our %EXPORT_TAGS = ( # 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}$/; -# Does not the length and number of the hex groups, nor the value ranges in the IPv4 groups +# 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]{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; diff --git a/t/config.t b/t/config.t index f786daa5d..5dde2c5bc 100644 --- a/t/config.t +++ b/t/config.t @@ -212,7 +212,7 @@ subtest 'Everything but NoWarnings' => sub { }; Zonemaster::Backend::Config->parse( $text ); } - qr{DB\.polling_interval.*hourly}, 'die: Invalid DB.polling_inteval value'; + qr{DB\.polling_interval.*hourly}, 'die: Invalid DB.polling_interval value'; throws_ok { my $text = q{ From eeab2f15594f9795da871de6972fff776e03940d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 9 Jun 2021 15:34:53 +0200 Subject: [PATCH 011/424] Add support for fake SHA-384 digests --- lib/Zonemaster/Backend/Validator.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index c7dd1b01d..031dcbe3d 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -50,7 +50,7 @@ Readonly my $IPV6_RE => qr/^([0-9a-f]{1,4}:[0-9a-f:]{1,}(:[0-9]{1,3}\.[0-9]{1,3} 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 $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; From cd0f7329bb023c2003f99cea90bf95a84bb58ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 7 Jun 2021 16:28:05 +0200 Subject: [PATCH 012/424] Fix handling of nameserver option in zmb --- script/zmb | 1 + 1 file changed, 1 insertion(+) diff --git a/script/zmb b/script/zmb index 72d8e0dd3..47f91981b 100755 --- a/script/zmb +++ b/script/zmb @@ -214,6 +214,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 = "" if not defined $ip; push @nameserver_objects, { ns => $domain, From 55868544f282d8f33dce283935768476c83165c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 8 Jun 2021 09:06:50 +0200 Subject: [PATCH 013/424] update command doc --- script/zmb | 1 + 1 file changed, 1 insertion(+) diff --git a/script/zmb b/script/zmb index 47f91981b..480e7edc7 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 From 28d57212036670aa724383867f46aed5d6320bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 9 Jun 2021 11:03:09 +0200 Subject: [PATCH 014/424] refactor ip default value --- script/zmb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/zmb b/script/zmb index 480e7edc7..c9b2192ec 100755 --- a/script/zmb +++ b/script/zmb @@ -215,7 +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 = "" if not defined $ip; + $ip //= ""; push @nameserver_objects, { ns => $domain, From 730a1dd88d7d6c3fb45232b82c1206db7b7e0a59 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 9 Jun 2021 19:47:43 +0200 Subject: [PATCH 015/424] Updates license statements --- LICENSE | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 From 4808085fe42a99d59a3dc29875effb6050648c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 9 Jun 2021 21:42:37 +0200 Subject: [PATCH 016/424] Update lower bound of number_of_processes_for_batch_testing It's documented as non-negative. Having it so makes sense because otherwise it's impossible to have a single test agent worker. Which is just aribtrary. --- lib/Zonemaster/Backend/Config.pm | 2 +- t/config.t | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 9dd385eaf..5ea6d3d36 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -495,7 +495,7 @@ UNITCHECK { _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_unsigned_int ); _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue', \&untaint_unsigned_int ); _create_setter( '_set_ZONEMASTER_number_of_processes_for_frontend_testing', '_ZONEMASTER_number_of_processes_for_frontend_testing', \&untaint_positive_int ); - _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing', \&untaint_positive_int ); + _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing', \&untaint_unsigned_int ); _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_positive_int ); } diff --git a/t/config.t b/t/config.t index 5dde2c5bc..3a2e7c100 100644 --- a/t/config.t +++ b/t/config.t @@ -400,11 +400,11 @@ subtest 'Everything but NoWarnings' => sub { engine = SQLite [ZONEMASTER] - number_of_processes_for_batch_testing = 0 + number_of_processes_for_batch_testing = 100000 }; Zonemaster::Backend::Config->parse( $text ); } - qr{ZONEMASTER\.number_of_processes_for_batch_testing.*0}, 'die: Invalid ZONEMASTER.number_of_processes_for_batch_testing value'; + qr{ZONEMASTER\.number_of_processes_for_batch_testing.*100000}, 'die: Invalid ZONEMASTER.number_of_processes_for_batch_testing value'; throws_ok { my $text = q{ From f4a2927b6511b1e53586fe7cbc8243f59bebaad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 9 Jun 2021 21:55:03 +0200 Subject: [PATCH 017/424] Update lower bound of max_zonemaster_execution_time It's documented as positive. It doesn't really make sense to allow zero values for this property. --- lib/Zonemaster/Backend/Config.pm | 2 +- t/config.t | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 5ea6d3d36..7be8e8ed5 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -491,7 +491,7 @@ UNITCHECK { _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_unsigned_int ); + _create_setter( '_set_ZONEMASTER_max_zonemaster_execution_time', '_ZONEMASTER_max_zonemaster_execution_time', \&untaint_positive_int ); _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_unsigned_int ); _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue', \&untaint_unsigned_int ); _create_setter( '_set_ZONEMASTER_number_of_processes_for_frontend_testing', '_ZONEMASTER_number_of_processes_for_frontend_testing', \&untaint_positive_int ); diff --git a/t/config.t b/t/config.t index 3a2e7c100..04e652a15 100644 --- a/t/config.t +++ b/t/config.t @@ -352,11 +352,11 @@ subtest 'Everything but NoWarnings' => sub { engine = SQLite [ZONEMASTER] - max_zonemaster_execution_time = -1 + max_zonemaster_execution_time = 0 }; Zonemaster::Backend::Config->parse( $text ); } - qr{ZONEMASTER\.max_zonemaster_execution_time.*-1}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value'; + qr{ZONEMASTER\.max_zonemaster_execution_time.*0}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value'; throws_ok { my $text = q{ From 443e6f65a278d6bad38f4af811f6fa3dacd9ffe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 10 Jun 2021 09:48:26 +0200 Subject: [PATCH 018/424] Refactor validation regexes --- lib/Zonemaster/Backend/Validator.pm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 031dcbe3d..2bb14e8f7 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -69,11 +69,8 @@ Readonly my $MILLIS_RE => qr/^(?:0|[1-9][0-9]{0,4})(?:[.][0-9]{1,3})?$/; # Printable ASCII but first character must not be space or '<' Readonly my $PASSWORD_RE => qr/^(?:[\x21-\x3b\x3d-\x7e][\x20-\x7e]{0,99})?$/; -# Allow for 5 significant digits -Readonly my $POSITIVE_INT_RE => qr/^[1-9][0-9]{0,4}$/; - # At least one non-zero digit -Readonly my $POSITIVE_NUM_RE => qr/[^0.]/; +Readonly my $NON_ZERO_NUM_RE => qr/[1-9]/; # 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; @@ -81,7 +78,7 @@ Readonly my $PROFILE_NAME_RE => qr/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; -# Allow for 5 significant digits +# Up to 5 digits Readonly my $UNSIGNED_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; @@ -212,12 +209,12 @@ sub untaint_password { sub untaint_positive_int { my ( $value ) = @_; - return _untaint_pat( $value, $POSITIVE_INT_RE ); + return _untaint_pat( $value, $UNSIGNED_INT_RE, $NON_ZERO_NUM_RE ); } sub untaint_positive_millis { my ( $value ) = @_; - return _untaint_pat( $value, $MILLIS_RE, $POSITIVE_NUM_RE ); + return _untaint_pat( $value, $MILLIS_RE, $NON_ZERO_NUM_RE ); } sub untaint_postgresql_ident { From e8eb75044e464a2c3932978ff209826583a83d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 10 Jun 2021 14:00:24 +0200 Subject: [PATCH 019/424] Use "non-negative" instead of "unsigned" --- docs/API.md | 12 ++++++------ lib/Zonemaster/Backend/Config.pm | 6 +++--- lib/Zonemaster/Backend/Validator.pm | 14 +++++++------- script/zmb | 6 +++--- t/validator.t | 16 ++++++++-------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/API.md b/docs/API.md index f838d2f96..f09f36c67 100644 --- a/docs/API.md +++ b/docs/API.md @@ -287,11 +287,11 @@ A default installation will accept the following `language tags`: * `sv` or `sv_SE` for Swedish language. -### Unsigned integer +### Non-negative integer Basic data type: number (integer) -An unsigned integer is either positive or zero. +An non-negative integer is either positive or zero. ### Username @@ -865,8 +865,8 @@ Example response: 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"`: An *non-negative integer*, optional. (default: 0). Position of the first returned element from the database returned list. +* `"limit"`: An *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. @@ -1097,8 +1097,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"`: an *non-negative integer*. The number of finished tests. +* `"nb_running"`: an *non-negative integer*. The number of running tests. * `"finished_test_ids"`: a list of *test ids*. The set of finished *tests* in this *batch*. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 7be8e8ed5..86536ce4f 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -492,10 +492,10 @@ UNITCHECK { _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_positive_int ); - _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_unsigned_int ); - _create_setter( '_set_ZONEMASTER_lock_on_queue', '_ZONEMASTER_lock_on_queue', \&untaint_unsigned_int ); + _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_non_negative_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_positive_int ); - _create_setter( '_set_ZONEMASTER_number_of_processes_for_batch_testing', '_ZONEMASTER_number_of_processes_for_batch_testing', \&untaint_unsigned_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_positive_int ); } diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 2bb14e8f7..e11267868 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -21,7 +21,7 @@ our @EXPORT_OK = qw( untaint_positive_int untaint_positive_millis untaint_postgresql_ident - untaint_unsigned_int + untaint_non_negative_int ); our %EXPORT_TAGS = ( @@ -36,7 +36,7 @@ our %EXPORT_TAGS = ( untaint_positive_int untaint_positive_millis untaint_postgresql_ident - untaint_unsigned_int + untaint_non_negative_int ) ], ); @@ -79,8 +79,8 @@ Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; # Up to 5 digits -Readonly my $UNSIGNED_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; -Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; +Readonly my $NON_NEGATIVE_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; +Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; sub joi { return JSON::Validator::Joi->new; @@ -209,7 +209,7 @@ sub untaint_password { sub untaint_positive_int { my ( $value ) = @_; - return _untaint_pat( $value, $UNSIGNED_INT_RE, $NON_ZERO_NUM_RE ); + return _untaint_pat( $value, $NON_NEGATIVE_INT_RE, $NON_ZERO_NUM_RE ); } sub untaint_positive_millis { @@ -222,9 +222,9 @@ sub untaint_postgresql_ident { return _untaint_pat( $value, $POSTGRESQL_IDENT_RE ); } -sub untaint_unsigned_int { +sub untaint_non_negative_int { my ( $value ) = @_; - return _untaint_pat( $value, $UNSIGNED_INT_RE ); + return _untaint_pat( $value, $NON_NEGATIVE_INT_RE ); } sub _untaint_pat { diff --git a/script/zmb b/script/zmb index 72d8e0dd3..611e0b03f 100755 --- a/script/zmb +++ b/script/zmb @@ -154,9 +154,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 diff --git a/t/validator.t b/t/validator.t index 1424bc1d4..07ae667b9 100644 --- a/t/validator.t +++ b/t/validator.t @@ -154,13 +154,13 @@ subtest 'Everything but NoWarnings' => sub { ok !tainted( untaint_postgresql_ident( taint( 'zonemaster' ) ) ), 'launder taint'; }; - subtest 'untaint_unsigned_int' => sub { - is scalar untaint_unsigned_int( '1' ), '1', 'accept: 1'; - is scalar untaint_unsigned_int( '0' ), '0', 'accept: 0'; - is scalar untaint_unsigned_int( '99999' ), '99999', 'accept: 99999'; - is scalar untaint_unsigned_int( '100000' ), undef, 'reject: 100000'; - is scalar untaint_unsigned_int( '0.5' ), undef, 'reject: 0.5'; - is scalar untaint_unsigned_int( '-1' ), undef, 'reject: -1'; - ok !tainted( untaint_unsigned_int( taint( '1' ) ) ), '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'; }; }; From af8c06f5e59970175057c034b724fa2d25071362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 10 Jun 2021 14:33:44 +0200 Subject: [PATCH 020/424] Accomodate the view that zero is both positive and negative --- docs/API.md | 4 +-- docs/Configuration.md | 8 +++--- lib/Zonemaster/Backend/Config.pm | 10 ++++---- lib/Zonemaster/Backend/Validator.pm | 12 ++++----- t/validator.t | 40 ++++++++++++++--------------- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/docs/API.md b/docs/API.md index f09f36c67..148d1668e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -88,7 +88,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*. @@ -291,8 +291,6 @@ A default installation will accept the following `language tags`: Basic data type: number (integer) -An non-negative integer is either positive or zero. - ### Username diff --git a/docs/Configuration.md b/docs/Configuration.md index 082a2f096..927d7375a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -61,7 +61,7 @@ over this. ### polling_interval -A positive decimal number. Max 5 and 3 digits in the integer and fraction +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. @@ -283,7 +283,7 @@ The ZONEMASTER section has several keys : ### max_zonemaster_execution_time -A positive integer. Max length 5 digits. +A strictly positive integer. Max length 5 digits. Time in seconds before reporting an unfinished test as failed. Default value: `600`. @@ -301,7 +301,7 @@ Do not use it (keep the default value "0"), or use it with care. ### number_of_processes_for_frontend_testing -A positive integer. Max length 5 digits. +A strictly positive integer. Max length 5 digits. Number of processes allowed to run in parallel (added with `number_of_processes_for_batch_testing`). @@ -332,7 +332,7 @@ Default value: `0`. ### age_reuse_previous_test -A positive integer. Max length 5 digits. +A strictly positive integer. Max length 5 digits. The shelf life of a test in seconds after its creation. Default value: `600`. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 86536ce4f..b8cbcb54f 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -425,7 +425,7 @@ Returns an integer. Get the value of L. -Returns a positive integer. +Returns a strictly positive integer. =head2 ZONEMASTER_number_of_processes_for_batch_testing @@ -481,7 +481,7 @@ sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMA # Compile time generation of setters for the properties documented above UNITCHECK { - _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_positive_millis ); + _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_strictly_positive_millis ); _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_ldh_domain ); _create_setter( '_set_MYSQL_user', '_MYSQL_user', \&untaint_mariadb_user ); _create_setter( '_set_MYSQL_password', '_MYSQL_password', \&untaint_password ); @@ -491,12 +491,12 @@ UNITCHECK { _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_positive_int ); + _create_setter( '_set_ZONEMASTER_max_zonemaster_execution_time', '_ZONEMASTER_max_zonemaster_execution_time', \&untaint_strictly_positive_int ); _create_setter( '_set_ZONEMASTER_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_non_negative_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_positive_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_positive_int ); + _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_strictly_positive_int ); } =head2 Language_Locale_hash diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index e11267868..bcc8c0cd3 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -18,8 +18,8 @@ our @EXPORT_OK = qw( untaint_mariadb_database untaint_mariadb_user untaint_password - untaint_positive_int - untaint_positive_millis + untaint_strictly_positive_int + untaint_strictly_positive_millis untaint_postgresql_ident untaint_non_negative_int ); @@ -33,8 +33,8 @@ our %EXPORT_TAGS = ( untaint_mariadb_database untaint_mariadb_user untaint_password - untaint_positive_int - untaint_positive_millis + untaint_strictly_positive_int + untaint_strictly_positive_millis untaint_postgresql_ident untaint_non_negative_int ) @@ -207,12 +207,12 @@ sub untaint_password { return _untaint_pat( $value, $PASSWORD_RE ); } -sub untaint_positive_int { +sub untaint_strictly_positive_int { my ( $value ) = @_; return _untaint_pat( $value, $NON_NEGATIVE_INT_RE, $NON_ZERO_NUM_RE ); } -sub untaint_positive_millis { +sub untaint_strictly_positive_millis { my ( $value ) = @_; return _untaint_pat( $value, $MILLIS_RE, $NON_ZERO_NUM_RE ); } diff --git a/t/validator.t b/t/validator.t index 07ae667b9..2c890986a 100644 --- a/t/validator.t +++ b/t/validator.t @@ -114,28 +114,28 @@ subtest 'Everything but NoWarnings' => sub { ok !tainted( untaint_password( taint( '123456' ) ) ), 'launder taint'; }; - subtest 'untaint_positive_int' => sub { - is scalar untaint_positive_int( '1' ), '1', 'accept: 1'; - is scalar untaint_positive_int( '99999' ), '99999', 'accept: 99999'; - is scalar untaint_positive_int( '100000' ), undef, 'reject: 100000'; - is scalar untaint_positive_int( '0' ), undef, 'reject: 0'; - is scalar untaint_positive_int( '0.5' ), undef, 'reject: 0.5'; - is scalar untaint_positive_int( '-1' ), undef, 'reject: -1'; - ok !tainted( untaint_positive_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_positive_millis' => sub { - is scalar untaint_positive_millis( '0.5' ), '0.5', 'accept: 0.5'; - is scalar untaint_positive_millis( '0.001' ), '0.001', 'accept: 0.001'; - is scalar untaint_positive_millis( '99999.999' ), '99999.999', 'accept: 99999.999'; - is scalar untaint_positive_millis( '1' ), '1', 'accept: 1'; - is scalar untaint_positive_millis( '99999' ), '99999', 'accept: 99999'; - is scalar untaint_positive_millis( '0.0009' ), undef, 'reject: 0.0009'; - is scalar untaint_positive_millis( '100000' ), undef, 'reject: 100000'; - is scalar untaint_positive_millis( '0' ), undef, 'reject: 0'; - is scalar untaint_positive_millis( '0.0' ), undef, 'reject: 0.0'; - is scalar untaint_positive_millis( '-1' ), undef, 'reject: -1'; - ok !tainted( untaint_positive_millis( taint( '0.5' ) ) ), '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'; }; subtest 'untaint_postgresql_ident' => sub { From 71bda0ce80d12d285007752bb658d00f06687656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 10 Jun 2021 16:59:44 +0200 Subject: [PATCH 021/424] Specify type of scalar in POD for properties --- lib/Zonemaster/Backend/Config.pm | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index b8cbcb54f..610b0b0ba 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -367,57 +367,77 @@ sub _set_DB_engine { Get the value of L. +Returns a number. + =head2 MYSQL_database Get the value of L. +Returns a string. + =head2 MySQL_host Get the value of L. +Returns a string. + =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_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 ZONEMASTER_max_zonemaster_execution_time Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_number_of_processes_for_frontend_testing @@ -425,7 +445,7 @@ Returns an integer. Get the value of L. -Returns a strictly positive integer. +Returns a number. =head2 ZONEMASTER_number_of_processes_for_batch_testing @@ -433,7 +453,7 @@ Returns a strictly positive integer. Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_lock_on_queue @@ -441,7 +461,7 @@ Returns an integer. Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_maximal_number_of_retries @@ -449,7 +469,7 @@ Returns an integer. Get the value of L. -Returns an integer. +Returns a number. =head2 ZONEMASTER_age_reuse_previous_test @@ -457,7 +477,7 @@ Returns an integer. Get the value of L. -Returns an integer. +Returns a number. =cut From cc312d105cd8cf5dbe9d81c4244e10817b2cb0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 10:13:03 +0200 Subject: [PATCH 022/424] Allow IPv6 addresses in MYSQL.host and POSTGRESQL.host --- lib/Zonemaster/Backend/Config.pm | 4 +- lib/Zonemaster/Backend/Validator.pm | 65 ++++++++++++++++++++++++----- t/validator.t | 13 ++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 610b0b0ba..b52b79ba2 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -502,11 +502,11 @@ sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMA # Compile time generation of setters for the properties documented above UNITCHECK { _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_strictly_positive_millis ); - _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_ldh_domain ); + _create_setter( '_set_MYSQL_host', '_MYSQL_host', \&untaint_host ); _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_ldh_domain ); + _create_setter( '_set_POSTGRESQL_host', '_POSTGRESQL_host', \&untaint_host ); _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 ); diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index bcc8c0cd3..ab47f6c16 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -10,33 +10,38 @@ use Exporter qw( import ); use File::Spec::Functions qw( file_name_is_absolute ); use JSON::Validator::Joi; use Readonly; +use Zonemaster::Engine::Net::IP; our @EXPORT_OK = qw( untaint_abs_path - untaint_ldh_domain untaint_engine_type + untaint_ip_address + untaint_host + untaint_ldh_domain untaint_mariadb_database untaint_mariadb_user + untaint_non_negative_int untaint_password + untaint_postgresql_ident untaint_strictly_positive_int untaint_strictly_positive_millis - untaint_postgresql_ident - untaint_non_negative_int ); our %EXPORT_TAGS = ( untaint => [ qw( untaint_abs_path - untaint_ldh_domain untaint_engine_type + untaint_ip_address + untaint_host + untaint_ldh_domain untaint_mariadb_database untaint_mariadb_user + untaint_non_negative_int untaint_password + untaint_postgresql_ident untaint_strictly_positive_int untaint_strictly_positive_millis - untaint_postgresql_ident - untaint_non_negative_int ) ], ); @@ -175,11 +180,6 @@ sub untaint_abs_path { return _untaint_pred( $value, \&file_name_is_absolute ); } -sub untaint_ldh_domain { - my ( $value ) = @_; - return _untaint_pat( $value, $LDH_DOMAIN_RE1, $LDH_DOMAIN_RE2 ); -} - =head2 untaint_engine_type Accepts the strings C<"MySQL">, C<"PostgreSQL"> and C<"SQLite">, @@ -192,6 +192,49 @@ 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 ) = @_; + if ( $value =~ /($IPV4_RE)/ + && Zonemaster::Engine::Net::IP::ip_is_ipv4( $value ) ) + { + return $1; + } + 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 ); +} + sub untaint_mariadb_database { my ( $value ) = @_; return _untaint_pat( $value, $MARIADB_IDENT_RE, $MARIADB_DATABASE_LENGTH_RE ); diff --git a/t/validator.t b/t/validator.t index 2c890986a..b5228f551 100644 --- a/t/validator.t +++ b/t/validator.t @@ -66,6 +66,19 @@ subtest 'Everything but NoWarnings' => sub { 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.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'; + ok !tainted( untaint_ip_address( taint( '192.0.2.1' ) ) ), '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'; From 5545f232efbab9ed3f567401ff985f14db9fc6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 10:54:23 +0200 Subject: [PATCH 023/424] Fix grammar --- docs/API.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/API.md b/docs/API.md index 148d1668e..0a8203ad2 100644 --- a/docs/API.md +++ b/docs/API.md @@ -863,8 +863,8 @@ Example response: An object with the following properties: -* `"offset"`: An *non-negative integer*, optional. (default: 0). Position of the first returned element from the database returned list. -* `"limit"`: An *non-negative 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. @@ -1095,8 +1095,8 @@ An object with the property: An object with the following properties: -* `"nb_finished"`: an *non-negative integer*. The number of finished tests. -* `"nb_running"`: an *non-negative 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*. From e4c1f6792ecd699c029eba4e884bba8c539d2b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 11:41:57 +0200 Subject: [PATCH 024/424] Properly encode IPv6 in MySQL DSN --- lib/Zonemaster/Backend/DB/MySQL.pm | 6 ++++++ lib/Zonemaster/Backend/Validator.pm | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ca7467475..ba80256e0 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -13,6 +13,7 @@ use JSON::PP; use Log::Any qw($log); use Zonemaster::Backend::Config; +use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); with 'Zonemaster::Backend::DB'; @@ -41,6 +42,11 @@ sub dbh { my $password = $self->config->MYSQL_password(); $log->notice( "Connecting to MySQL: database=$database host=$host user=$user" ) if $log->is_notice; + + if ( untaint_ipv6_address( $host ) ) { + $host = "[$host]"; + } + $dbh = DBI->connect( "DBI:mysql:database=$database;host=$host", $user, diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index ab47f6c16..d9f062c05 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -16,6 +16,8 @@ our @EXPORT_OK = qw( untaint_abs_path untaint_engine_type untaint_ip_address + untaint_ipv4_address + untaint_ipv6_address untaint_host untaint_ldh_domain untaint_mariadb_database @@ -33,6 +35,8 @@ our %EXPORT_TAGS = ( untaint_abs_path untaint_engine_type untaint_ip_address + untaint_ipv4_address + untaint_ipv6_address untaint_host untaint_ldh_domain untaint_mariadb_database @@ -199,12 +203,34 @@ 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 ) ) { From 401331a1331623af3ebd9996f5c5a2bb7fb6d4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 12:50:21 +0200 Subject: [PATCH 025/424] Allow ::1 in {MYSQL,POSTGRESQL}.host --- lib/Zonemaster/Backend/Validator.pm | 2 +- t/validator.t | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index d9f062c05..ea42cb70c 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -54,7 +54,7 @@ our %EXPORT_TAGS = ( Readonly my $IPV4_RE => qr/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/; # 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]{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 $IPV6_RE => qr/^[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; diff --git a/t/validator.t b/t/validator.t index b5228f551..e09af2722 100644 --- a/t/validator.t +++ b/t/validator.t @@ -76,6 +76,7 @@ subtest 'Everything but NoWarnings' => sub { 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'; ok !tainted( untaint_ip_address( taint( '192.0.2.1' ) ) ), 'launder taint'; }; From ed16861f665daf5b75ef40016087a3e8ed400988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 12:59:06 +0200 Subject: [PATCH 026/424] Clarify the meaning of non-negative number --- docs/API.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/API.md b/docs/API.md index 0a8203ad2..f5a0184db 100644 --- a/docs/API.md +++ b/docs/API.md @@ -291,6 +291,8 @@ A default installation will accept the following `language tags`: Basic data type: number (integer) +A non-negative integer is either zero or strictly positive. + ### Username From c0639c0b1a39a2f45aac5b686c790177d2cdecf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 13:02:25 +0200 Subject: [PATCH 027/424] Note that {MYSQL,POSTGRESQL}.host accept IP addresses --- docs/Configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 927d7375a..e497cc7ea 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -74,7 +74,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An [LDH domain name]. +An [LDH domain name] or IP address. The host name of the machine on which the MySQL server is running. @@ -115,7 +115,7 @@ Available keys : `host`, `user`, `password`, `database`. ### host -An [LDH domain name]. +An [LDH domain name] or IP address. The host name of the machine on which the PostgreSQL server is running. From ced80bbbc0b36fbf2e0a6af9ac5541b42cc4ab93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 13:17:58 +0200 Subject: [PATCH 028/424] Order regexes lexicographically --- lib/Zonemaster/Backend/Validator.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index ea42cb70c..60476df19 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -75,21 +75,21 @@ 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})?$/; -# Printable ASCII but first character must not be space or '<' -Readonly my $PASSWORD_RE => qr/^(?:[\x21-\x3b\x3d-\x7e][\x20-\x7e]{0,99})?$/; +# 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}$/; - -# Up to 5 digits -Readonly my $NON_NEGATIVE_INT_RE => qr/^(?:0|[1-9][0-9]{0,4})$/; -Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; +Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; sub joi { return JSON::Validator::Joi->new; From 3566ffdfab4dff06aebe8de892f83a683680db98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 13:20:48 +0200 Subject: [PATCH 029/424] Clean up some whitespace --- lib/Zonemaster/Backend/Validator.pm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 60476df19..ad7071221 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -263,37 +263,37 @@ sub untaint_ldh_domain { sub untaint_mariadb_database { my ( $value ) = @_; - return _untaint_pat( $value, $MARIADB_IDENT_RE, $MARIADB_DATABASE_LENGTH_RE ); + 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 ); + return _untaint_pat( $value, $MARIADB_IDENT_RE, $MARIADB_USER_LENGTH_RE ); } sub untaint_password { my ( $value ) = @_; - return _untaint_pat( $value, $PASSWORD_RE ); + 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 ); + 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 ); + return _untaint_pat( $value, $MILLIS_RE, $NON_ZERO_NUM_RE ); } sub untaint_postgresql_ident { my ( $value ) = @_; - return _untaint_pat( $value, $POSTGRESQL_IDENT_RE ); + return _untaint_pat( $value, $POSTGRESQL_IDENT_RE ); } sub untaint_non_negative_int { my ( $value ) = @_; - return _untaint_pat( $value, $NON_NEGATIVE_INT_RE ); + return _untaint_pat( $value, $NON_NEGATIVE_INT_RE ); } sub _untaint_pat { From 0eceff01ad86c0ab20205d537ae693af3b1f86fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 11 Jun 2021 13:48:57 +0200 Subject: [PATCH 030/424] Order tests lexicographically --- t/validator.t | 70 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/t/validator.t b/t/validator.t index e09af2722..15e52c73a 100644 --- a/t/validator.t +++ b/t/validator.t @@ -44,17 +44,6 @@ subtest 'Everything but NoWarnings' => sub { ok !tainted( untaint_abs_path( taint( 'localhost' ) ) ), '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_engine_type' => sub { is scalar untaint_engine_type( 'MySQL' ), 'MySQL', 'accept: MySQL'; is scalar untaint_engine_type( 'mysql' ), 'mysql', 'accept: mysql'; @@ -80,6 +69,17 @@ subtest 'Everything but NoWarnings' => sub { 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_mariadb_database' => sub { is scalar untaint_mariadb_database( 'zonemaster' ), 'zonemaster', 'accept: zonemaster'; is scalar untaint_mariadb_database( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER'; @@ -128,30 +128,6 @@ subtest 'Everything but NoWarnings' => sub { ok !tainted( untaint_password( taint( '123456' ) ) ), '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'; - }; - subtest 'untaint_postgresql_ident' => sub { is scalar untaint_postgresql_ident( 'zonemaster' ), 'zonemaster', 'accept: zonemaster'; is scalar untaint_postgresql_ident( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER'; @@ -177,4 +153,28 @@ subtest 'Everything but NoWarnings' => sub { 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'; + }; }; From 4ba93fcf7b09e1ad63ed82d9ca8b8318e87f73cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 14 Jun 2021 13:19:56 +0200 Subject: [PATCH 031/424] Add another test --- t/validator.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/validator.t b/t/validator.t index 15e52c73a..ac523f9aa 100644 --- a/t/validator.t +++ b/t/validator.t @@ -66,6 +66,7 @@ subtest 'Everything but NoWarnings' => sub { 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'; }; From a9a1b263e521e65a3217946116d72fae85ca67c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 14 Jun 2021 15:22:08 +0200 Subject: [PATCH 032/424] Deprecate --testid params in zmb --- script/zmb | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/script/zmb b/script/zmb index 3ee8ad40f..75aba587e 100755 --- a/script/zmb +++ b/script/zmb @@ -248,7 +248,8 @@ sub cmd_start_domain_test { zmb [GLOBAL OPTIONS] test_progress [OPTIONS] Options: - --testid TEST_ID + --test-id TEST_ID + --testid TEST_ID # deprecated, use --test-id instead =cut @@ -257,14 +258,26 @@ sub cmd_test_progress { 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, + 'testid|t=s' => \$opt_testid, + ) or pod2usage( 2 ); + + if ( defined $opt_testid ) { + warn( "The --testid parameter deprecated. Use --test-id instead.\n" ); + } + if ( defined $opt_testid && defined $opt_test_id ) { + pod2usage( "The --testid parameter is forbidden when --test-id is present." ); + } + $opt_test_id //= $opt_testid; return to_jsonrpc( id => 1, method => 'test_progress', params => { - test_id => $opt_testid, + test_id => $opt_test_id, }, ); } @@ -303,7 +316,8 @@ sub cmd_get_test_params { zmb [GLOBAL OPTIONS] get_test_results [OPTIONS] Options: - --testid TEST_ID + --test-id TEST_ID + --testid TEST_ID # deprecated, use --test-id instead --lang LANGUAGE =cut @@ -313,17 +327,27 @@ sub cmd_get_test_results { my $opt_lang; my $opt_testid; + my $opt_test_id; GetOptionsFromArray( \@opts, - 'testid|t=s' => \$opt_testid, - 'lang|l=s' => \$opt_lang, + 'testid|t=s' => \$opt_testid, + 'test-id|t=s' => \$opt_test_id, + 'lang|l=s' => \$opt_lang, ) or pod2usage( 2 ); + if ( defined $opt_testid ) { + warn( "The --testid parameter deprecated. Use --test-id instead.\n" ); + } + if ( defined $opt_testid && defined $opt_test_id ) { + pod2usage( "The --testid parameter is forbidden when --test-id is present." ); + } + $opt_test_id //= $opt_testid; + return to_jsonrpc( id => 1, method => 'get_test_results', params => { - id => $opt_testid, + id => $opt_test_id, language => $opt_lang, }, ); From c3af6e90102eafe0a22bd56c89c1a0379bd0cdd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 14 Jun 2021 15:24:26 +0200 Subject: [PATCH 033/424] Replace deprecated parameters --- script/zmtest | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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}" From 0bacc767b70ce57caafecc4a16cbab6c3980a78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 15 Jun 2021 14:40:06 +0200 Subject: [PATCH 034/424] Avoid deprecating parameters in unstable interface --- script/zmb | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/script/zmb b/script/zmb index 75aba587e..8f82987f5 100755 --- a/script/zmb +++ b/script/zmb @@ -249,7 +249,6 @@ sub cmd_start_domain_test { Options: --test-id TEST_ID - --testid TEST_ID # deprecated, use --test-id instead =cut @@ -257,22 +256,12 @@ sub cmd_test_progress { my @opts = @_; my $opt_lang; - my $opt_testid; my $opt_test_id; GetOptionsFromArray( \@opts, 'test-id|t=s' => \$opt_test_id, - 'testid|t=s' => \$opt_testid, ) or pod2usage( 2 ); - if ( defined $opt_testid ) { - warn( "The --testid parameter deprecated. Use --test-id instead.\n" ); - } - if ( defined $opt_testid && defined $opt_test_id ) { - pod2usage( "The --testid parameter is forbidden when --test-id is present." ); - } - $opt_test_id //= $opt_testid; - return to_jsonrpc( id => 1, method => 'test_progress', @@ -317,7 +306,6 @@ sub cmd_get_test_params { Options: --test-id TEST_ID - --testid TEST_ID # deprecated, use --test-id instead --lang LANGUAGE =cut @@ -326,23 +314,13 @@ sub cmd_get_test_results { my @opts = @_; my $opt_lang; - my $opt_testid; my $opt_test_id; GetOptionsFromArray( \@opts, - 'testid|t=s' => \$opt_testid, 'test-id|t=s' => \$opt_test_id, 'lang|l=s' => \$opt_lang, ) or pod2usage( 2 ); - if ( defined $opt_testid ) { - warn( "The --testid parameter deprecated. Use --test-id instead.\n" ); - } - if ( defined $opt_testid && defined $opt_test_id ) { - pod2usage( "The --testid parameter is forbidden when --test-id is present." ); - } - $opt_test_id //= $opt_testid; - return to_jsonrpc( id => 1, method => 'get_test_results', From b6e50c0a52ceeac8faf11ae973c788fe59c24e45 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 16 Jun 2021 15:06:10 +0200 Subject: [PATCH 035/424] Adds documentation of camel case database engine name --- lib/Zonemaster/Backend/Config.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index b52b79ba2..7476100f7 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -765,6 +765,7 @@ 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', From 0f2c4020e38c053ea109a32ea758bc468aded0ac Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 29 Apr 2021 18:52:45 +0200 Subject: [PATCH 036/424] Harmonize code --- lib/Zonemaster/Backend/Config.pm | 2 +- lib/Zonemaster/Backend/DB/MySQL.pm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 7476100f7..87041b41f 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -377,7 +377,7 @@ Get the value of L. diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ba80256e0..7d67216de 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -38,8 +38,8 @@ sub 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(); + 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; From 776f670af2a9eb84c2be0c4cc49ded55aac1fa9a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 29 Apr 2021 18:31:54 +0200 Subject: [PATCH 037/424] Configuration option for MySQL database port --- lib/Zonemaster/Backend/Config.pm | 14 ++++++++++++++ lib/Zonemaster/Backend/DB/MySQL.pm | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 87041b41f..1e2aa1b7b 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -122,6 +122,7 @@ sub parse { # Assign default values $obj->_set_DB_polling_interval( '0.5' ); + $obj->_set_MYSQL_port( '3306' ); $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' ); @@ -200,6 +201,9 @@ 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' ) ) ) { + $obj->{_MYSQL_port} = $value; + } if ( defined( my $value = $get_and_clear->( 'MYSQL', 'user' ) ) ) { $obj->_set_MYSQL_user( $value ); } @@ -384,6 +388,14 @@ Get the value of L +property from the loaded config. + +Returns a number. + + =head2 MYSQL_password Get the value of L. @@ -484,6 +496,7 @@ Returns a number. # 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}; } @@ -503,6 +516,7 @@ sub ZONEMASTER_age_reuse_previous_test { return $_[0]->{_ZONEMA UNITCHECK { _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 ); diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 7d67216de..86c387eec 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -38,9 +38,12 @@ sub dbh { else { my $database = $self->config->MYSQL_database; my $host = $self->config->MYSQL_host; + my $port = $self->config->MYSQL_port; my $user = $self->config->MYSQL_user; my $password = $self->config->MYSQL_password; + my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; + $log->notice( "Connecting to MySQL: database=$database host=$host user=$user" ) if $log->is_notice; if ( untaint_ipv6_address( $host ) ) { @@ -48,7 +51,7 @@ sub dbh { } $dbh = DBI->connect( - "DBI:mysql:database=$database;host=$host", + $data_source_name, $user, $password, { From 548acae6ec5d6291c22ec2019d9db5fae048f00a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 29 Apr 2021 19:13:17 +0200 Subject: [PATCH 038/424] Configuration option for PostgreSQL database port --- lib/Zonemaster/Backend/Config.pm | 14 ++++++++++++++ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 1e2aa1b7b..ff99f51ee 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -123,6 +123,7 @@ 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' ); @@ -216,6 +217,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 ); } @@ -424,6 +428,14 @@ Get the value of L +property from the loaded config. + +Returns a number. + + =head2 POSTGRESQL_password Get the value of L. @@ -501,6 +513,7 @@ sub MYSQL_user { return $_[0]->{_MYSQL_ 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}; } @@ -521,6 +534,7 @@ UNITCHECK { _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 ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 570993af8..f94288406 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -37,12 +37,15 @@ sub dbh { else { my $database = $self->config->POSTGRESQL_database; my $host = $self->config->POSTGRESQL_host; + my $port = $self->config->POSTGRESQL_port; my $user = $self->config->POSTGRESQL_user; my $password = $self->config->POSTGRESQL_password; + my $data_source_name = "DBI:Pg:database=$database;host=$host;port=$port"; + $log->notice( "Connecting to PostgreSQL: database=$database host=$host user=$user" ) if $log->is_notice; $dbh = DBI->connect( - "DBI:Pg:database=$database;host=$host", + $data_source_name, $user, $password, { From 473225b4ffda41164fd154acceffc969bc397b9b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 29 Apr 2021 17:35:44 +0200 Subject: [PATCH 039/424] Documentation --- docs/Configuration.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index e497cc7ea..aa1d4bf00 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -70,7 +70,7 @@ Default value: `0.5`. ## MYSQL section -Available keys : `host`, `user`, `password`, `database`. +Available keys : `host`, `port`, `user`, `password`, `database`. ### host @@ -80,6 +80,13 @@ 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. If the server is on the local +machine, then the [MYSQL.host] value must contain the loopback IP +address (127.0.0.1). +Default value: `3306`. + ### user An ASCII-only [MariaDB unquoted identifier]. @@ -111,7 +118,7 @@ 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 @@ -121,6 +128,11 @@ 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. From e21ff1f973808778aef38e4b7d729edd5a8f10f4 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 29 Apr 2021 19:19:16 +0200 Subject: [PATCH 040/424] Unit test for database port --- t/config.t | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/t/config.t b/t/config.t index 04e652a15..590b81f71 100644 --- a/t/config.t +++ b/t/config.t @@ -24,12 +24,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 @@ -50,10 +52,12 @@ subtest 'Everything but NoWarnings' => sub { 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'; From 25d96aa331dd3dafff3ee1d3e3650ddc29031e6e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 15 Jun 2021 10:32:02 +0200 Subject: [PATCH 041/424] Add unit test on default port values --- t/config.t | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/t/config.t b/t/config.t index 590b81f71..ec0b33f25 100644 --- a/t/config.t +++ b/t/config.t @@ -80,12 +80,14 @@ 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->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'; + 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'; + is $config->MYSQL_port, 3306, 'default: MYSQL.port'; + is $config->POSTGRESQL_port, 5432, 'default: POSTGRESQL.port'; }; lives_and { From efccda9f10613cb2dbdf943cc0597a0d3f1c7b44 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 15 Jun 2021 10:38:19 +0200 Subject: [PATCH 042/424] Document MYSQL.host special case discarding MYSQL.port --- docs/Configuration.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index aa1d4bf00..4545af2b5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -80,13 +80,19 @@ 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. +If this property is set to `localhost`, then the [MYSQL.port] property +is discarded as the driver connects using a UNIX socket (see the +[DBD::mysql documentation]). + ### port -The port the MySQL server is listening on. If the server is on the local -machine, then the [MYSQL.host] value must contain the loopback IP -address (127.0.0.1). +The port the MySQL server is listening on. Default value: `3306`. +If [MYSQL.host] is set to `localhost`, then this property is discarded as +the driver connects using a UNIX socket (see the [DBD::mysql +documentation]). + ### user An ASCII-only [MariaDB unquoted identifier]. @@ -359,6 +365,7 @@ Otherwise a new test request is enqueued. [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 From 5da6c6ad5b8edb7a3e9f52189c4418b300784042 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 15 Jun 2021 11:03:36 +0200 Subject: [PATCH 043/424] Emits warning when MYSQL.port is discarded This can be the case if MYSQL.host holds the value `localhost', then the driver connects using a UNIX socket, see the DBD:mysql documentation --- lib/Zonemaster/Backend/Config.pm | 5 ++++- t/config.t | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index ff99f51ee..efccdefaa 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -136,7 +136,7 @@ sub parse { $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"; } @@ -203,6 +203,9 @@ sub parse { $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' ) ) ) { diff --git a/t/config.t b/t/config.t index ec0b33f25..7f9087309 100644 --- a/t/config.t +++ b/t/config.t @@ -164,6 +164,24 @@ subtest 'Everything but NoWarnings' => sub { is $config->ZONEMASTER_number_of_processes_for_batch_testing, '22', 'fallback: ZONEMASTER.number_of_processes_for_batch_testing'; }; + lives_and { + 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 ); From aeaf9da7a477741529654270438f70c0ee8ac8e9 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 16 Jun 2021 16:55:12 +0200 Subject: [PATCH 044/424] Improve documentation --- docs/Configuration.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 4545af2b5..ae23cb571 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -80,18 +80,14 @@ 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. -If this property is set to `localhost`, then the [MYSQL.port] property -is discarded as the driver connects using a UNIX socket (see the -[DBD::mysql documentation]). - ### port The port the MySQL server is listening on. Default value: `3306`. -If [MYSQL.host] is set to `localhost`, then this property is discarded as -the driver connects using a UNIX socket (see the [DBD::mysql -documentation]). +If [MYSQL.host] is set to `localhost` (but not `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 From abdee07a1f18b1bbad2a61e386527fb0f5e0b4ff Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 11:42:25 +0200 Subject: [PATCH 045/424] Fix grammar --- docs/Configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index ae23cb571..f18beca55 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -85,7 +85,7 @@ If this property is unspecified, the value of [DB.database_host] is used instead The port the MySQL server is listening on. Default value: `3306`. -If [MYSQL.host] is set to `localhost` (but not `127.0.0.1` nor `::1`), +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]). From 4637d5a3ae2d2e579adf794b31bf023653b1a3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 17 Jun 2021 12:09:48 +0200 Subject: [PATCH 046/424] Move installation considerations to its own sub-section --- docs/Configuration.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index e497cc7ea..335c624f4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -178,14 +178,6 @@ 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.) - Removing a language from the configuration file just blocks that language from being allowed. If there are more than one `locale tag` (with different country codes) for the same language, then @@ -241,6 +233,16 @@ 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. 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.) + 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. From db24ec5c0efc949279aee6355bc4b116068b418c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 12:54:28 +0200 Subject: [PATCH 047/424] Gather details on valid configuration values in one place --- docs/Configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 335c624f4..3cf9dfa09 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -169,6 +169,8 @@ 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}$/`. +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. @@ -225,8 +227,6 @@ The following `language tags` are generated: * sv * sv_SE -It is an error to repeat the same `locale tag`. - Setting in the default configuration file: ``` From 3648b0933cc1baf172e8b18a8e697dbe05c5070e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 17 Jun 2021 12:25:41 +0200 Subject: [PATCH 048/424] Make a section about what you get out of the box --- docs/Configuration.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 3cf9dfa09..76c306f3e 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -201,18 +201,6 @@ 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`. -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 @@ -227,6 +215,20 @@ The following `language tags` are generated: * sv * sv_SE +#### 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 + Setting in the default configuration file: ``` From 15eb4e96f1f2cda34b92d6b7950fa719c7458196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 12:54:28 +0200 Subject: [PATCH 049/424] Move documentation of language tag generation to API.md --- docs/API.md | 25 +++++++++++++++++++++++++ docs/Configuration.md | 26 -------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/docs/API.md b/docs/API.md index f5a0184db..ad1d18ff9 100644 --- a/docs/API.md +++ b/docs/API.md @@ -382,6 +382,31 @@ An array of *Profile names* in lower case. `"default"` is always included. Returns all valid [language tags][language tag] generated from the setting in the configuration file. +The `language tags` are generated from the configured [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`. + +The following `language tags` are generated using the default configuration: +* da +* da_DK +* en +* en_US +* fi +* fi_FI +* fr +* fr_FR +* nb +* nb_NO +* sv +* sv_SE + Example request: ```json { diff --git a/docs/Configuration.md b/docs/Configuration.md index 76c306f3e..75b72b285 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -189,32 +189,6 @@ English is the Zonemaster default language, but it can be blocked from being allowed by RPC-API by not including it in the configuration. -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`. - -The following `language tags` are generated: -* da -* da_DK -* en -* en_US -* fi -* fi_FI -* fr -* fr_FR -* nb -* nb_NO -* sv -* sv_SE - #### Out-of-the-box support The default installation and configuration supports the From d87a8baa76e081595f2cda61b7cd21b11ae51181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 12:54:28 +0200 Subject: [PATCH 050/424] Add sub-headings about usage and design --- docs/Configuration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Configuration.md b/docs/Configuration.md index 75b72b285..4eee66072 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -174,12 +174,16 @@ 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. +#### Usage + Removing a language from the configuration file just blocks that language from being allowed. If there are more than one `locale tag` (with different country codes) for the same language, then From 8cf38b50376463266f3cfb6dba3218d1d19f5584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 14:07:45 +0200 Subject: [PATCH 051/424] Integrate two lists of language tags --- docs/API.md | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/API.md b/docs/API.md index ad1d18ff9..be50b1dea 100644 --- a/docs/API.md +++ b/docs/API.md @@ -393,20 +393,6 @@ 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`. -The following `language tags` are generated using the default configuration: -* da -* da_DK -* en -* en_US -* fi -* fi_FI -* fr -* fr_FR -* nb -* nb_NO -* sv -* sv_SE - Example request: ```json { @@ -416,16 +402,22 @@ Example request: } ``` -Example response: +Example response (as generated using the default configuration): ```json { "jsonrpc": "2.0", "id": 1, "result": [ + "da", + "da_DK", "en", "en_US", + "fi", + "fi_FI", "fr", "fr_FR", + "nb", + "nb_NO", "sv", "sv_SE" ] From 8818755ac76e971dad607e5688e566e4a717dbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 17 Jun 2021 15:51:57 +0200 Subject: [PATCH 052/424] Fix link --- docs/API.md | 9 +++++---- docs/Configuration.md | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/API.md b/docs/API.md index be50b1dea..3efc16f95 100644 --- a/docs/API.md +++ b/docs/API.md @@ -382,9 +382,9 @@ An array of *Profile names* in lower case. `"default"` is always included. Returns all valid [language tags][language tag] generated from the setting in the configuration file. -The `language tags` are generated from the configured [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 +The `language tags` are generated from the [LANGUAGE.locale] property. +Each `locale tag` in LANGUAGE.locale generates 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`. @@ -1191,5 +1191,6 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [Available profiles]: Configuration.md#profiles-section [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 +[LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag +[Privilege levels]: #privilege-levels diff --git a/docs/Configuration.md b/docs/Configuration.md index 4eee66072..76016e73e 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -165,9 +165,10 @@ 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 space separated list of `locale tags` where each tag matches the regular +expression `/^[a-z]{2}_[A-Z]{2}$/`. It is an error to repeat the same `locale tag`. From b39c6fd431ba3336c86dfd1f201a397858ef82b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 17 Jun 2021 16:51:10 +0200 Subject: [PATCH 053/424] Combine two sentences into one --- docs/API.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 3efc16f95..5654bf485 100644 --- a/docs/API.md +++ b/docs/API.md @@ -379,10 +379,9 @@ 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 all valid [language tags][language tag] generated from the +[LANGUAGE.locale] property in the configuration file. -The `language tags` are generated from the [LANGUAGE.locale] property. Each `locale tag` in LANGUAGE.locale generates 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`. From 0b0d5af2ce1c68e5bb424819329c69a8821da3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 17 Jun 2021 17:41:14 +0200 Subject: [PATCH 054/424] Integrate language tag rules into data type documentation --- docs/API.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/docs/API.md b/docs/API.md index 5654bf485..88a3ae45a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -266,13 +266,16 @@ 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. +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 a single `locale tag` in LANGUAGE.locale. + (So that it is unambiguous which *locale tag* is matched.) -Any other string is an error. +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 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 @@ -379,18 +382,8 @@ 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 -[LANGUAGE.locale] property in the configuration file. - -Each `locale tag` in LANGUAGE.locale generates 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`. +Returns a list of all valid [language tags][language tag] that match `locale +tags` according to the rules defined by the [language tag] data type. Example request: ```json From 854c21e0f6774ddf441c6c2bac8424b0a86b6fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 18 Jun 2021 10:16:56 +0200 Subject: [PATCH 055/424] Mark data types with emphasis instead of code spans --- docs/API.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/API.md b/docs/API.md index 88a3ae45a..2f3d0999e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -266,22 +266,22 @@ Basic data type: string A string of A-Z, a-z and underscores matching the regular expression `/^[a-z]{2}(_[A-Z]{2})?$/`. -The set of valid `language tags` is further constrained by the +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 a single `locale tag` in LANGUAGE.locale. +* 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` +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 two first characters of the `language tag` are intended to be an +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`: +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. @@ -382,8 +382,7 @@ An array of *Profile names* in lower case. `"default"` is always included. ## API method: `get_language_tags` -Returns a list of all valid [language tags][language tag] that match `locale -tags` according to the rules defined by the [language tag] data type. +Returns a list of all valid [*language tags*][language tag]. Example request: ```json From 000522860a1b53b3a4e0be1c71eef4421a5b9f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 18 Jun 2021 11:24:21 +0200 Subject: [PATCH 056/424] Add sub-headings to language tag definition --- docs/API.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/API.md b/docs/API.md index 2f3d0999e..a3a5129ef 100644 --- a/docs/API.md +++ b/docs/API.md @@ -277,10 +277,14 @@ The set of valid *language tags* is further constrained by the 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". +#### 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*: * `da` or `da_DK` for Danish language. * `en` or `en_US` for English language. From 192f91ccdcdac2a9016ac6cc3bced0ec714c6887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 18 Jun 2021 11:25:37 +0200 Subject: [PATCH 057/424] Sort data types lexicographically --- docs/API.md | 84 ++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/API.md b/docs/API.md index a3a5129ef..49a325918 100644 --- a/docs/API.md +++ b/docs/API.md @@ -156,6 +156,41 @@ This parameter is a string that are an IPv4 or IPv6. It's validated with the fol - 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})$/` +### 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 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". + +#### 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*: +* `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. + + ### Name server Basic data type: object @@ -166,6 +201,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) @@ -259,48 +301,6 @@ 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 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". - -#### 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*: -* `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. - - -### Non-negative integer - -Basic data type: number (integer) - -A non-negative integer is either zero or strictly positive. - - ### Username Basic data type: string From 2d83009206c8ddebf01ecc5b7fb9f43b6575d6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 21:37:04 +0200 Subject: [PATCH 058/424] Update some markup --- docs/API.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 49a325918..e827e53cc 100644 --- a/docs/API.md +++ b/docs/API.md @@ -166,12 +166,12 @@ A string of A-Z, a-z and underscores matching the regular expression 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. + 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. + 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* +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". #### Design From e4d3a4a64f7f3ec9d9c08c7ac4eedd640d3ead7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 11:21:25 +0200 Subject: [PATCH 059/424] Add a note under get_language_tags --- docs/API.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index e827e53cc..43b6b18ec 100644 --- a/docs/API.md +++ b/docs/API.md @@ -386,7 +386,11 @@ An array of *Profile names* in lower case. `"default"` is always included. ## API method: `get_language_tags` -Returns a list of all valid [*language tags*][language tag]. +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 [*langauge tags*][Language tag]. Example request: ```json From 27119d7bfe3b6dfdabeb128d952e799237d444b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 14:07:26 +0200 Subject: [PATCH 060/424] Typo --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 43b6b18ec..ff10682d7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -390,7 +390,7 @@ 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 [*langauge tags*][Language tag]. +> is excluded from the set of valid [*language tags*][Language tag]. Example request: ```json From 523d14840f5bdd5d1d484ce092b33f2a170af9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 14:14:10 +0200 Subject: [PATCH 061/424] Make maintenance easier --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index ff10682d7..fd0d1def2 100644 --- a/docs/API.md +++ b/docs/API.md @@ -401,7 +401,7 @@ Example request: } ``` -Example response (as generated using the default configuration): +Example response: ```json { "jsonrpc": "2.0", From 2bd86bd10d43cd5752ae0a3d5dda0f57b7ba006d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 17:20:52 +0200 Subject: [PATCH 062/424] Tidy --- lib/Zonemaster/Backend/RPCAPI.pm | 9 +++------ lib/Zonemaster/Backend/TestAgent.pm | 16 ++++++++-------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 7ef40ca9c..0fc93d316 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -103,12 +103,9 @@ sub profile_names { my ( $self ) = @_; my @profiles; - eval { - @profiles = $self->{config}->ListPublicProfiles(); - - }; - if ($@) { - handle_exception('profile_names', $@, '004'); + eval { @profiles = $self->{config}->ListPublicProfiles() }; + if ( $@ ) { + handle_exception( 'profile_names', $@, '004' ); } return \@profiles; diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index d07f04062..38c1631ec 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -21,7 +21,7 @@ sub new { my ( $class, $params ) = @_; my $self = {}; - if ( ! $params || ! $params->{config} ) { + if ( !$params || !$params->{config} ) { die "missing 'config' parameter"; } @@ -29,7 +29,7 @@ sub new { my $dbtype; if ( $params->{dbtype} ) { - $dbtype = $self->{config}->check_db($params->{dbtype}); + $dbtype = $self->{config}->check_db( $params->{dbtype} ); } else { $dbtype = $self->{config}->DB_engine; @@ -40,13 +40,13 @@ sub new { $self->{db} = $backend_module->new( { config => $self->{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 ); + 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') { + elsif ( $profile ne 'default' ) { die "the profile definition json file of the profile [$profile] defined in the backend config file can't be read"; } } From 1df61f3e01143f7f4767672d6d15ad71534486b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 14:22:16 +0200 Subject: [PATCH 063/424] Change internal profiles representation --- lib/Zonemaster/Backend/Config.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index efccdefaa..ada982790 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -266,7 +266,7 @@ sub parse { } $obj->{_public_profiles} = { - default => '', + default => undef, }; for my $name ( $ini->Parameters( 'PUBLIC PROFILES' ) ) { $obj->{_public_profiles}{lc $name} = $get_and_clear->( 'PUBLIC PROFILES', $name ); @@ -622,7 +622,7 @@ sub ReadProfilesInfo { 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}; + $profiles->{$public_profile}->{profile_file_name} = $self->{_public_profiles}{$public_profile} // ""; } foreach my $private_profile ( keys %{ $self->{_private_profiles} } ) { From aa244ee9b4839944d037e0be2e68d6b89f4075bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 16:21:09 +0200 Subject: [PATCH 064/424] Shave some unused details from member variable --- lib/Zonemaster/Backend/TestAgent.pm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 38c1631ec..75f2b099d 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -25,25 +25,25 @@ sub new { 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} } ); + $self->{db} = $backend_module->new( { 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' ); + my %all_profiles = %{ $config->ReadProfilesInfo() }; + foreach my $profile ( keys %all_profiles ) { + die "default profile cannot be private" if ( $profile eq 'default' && $all_profiles{$profile}{type} eq 'private' ); + if ( -e $all_profiles{$profile}{profile_file_name} ) { + my $json = read_file( $all_profiles{$profile}{profile_file_name}, err_mode => 'croak' ); $self->{profiles}{$profile}{zm_profile} = Zonemaster::Engine::Profile->from_json( $json ); } elsif ( $profile ne 'default' ) { From f81f283596b5e28ec7e677c4853c6014dc30c787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 16:33:50 +0200 Subject: [PATCH 065/424] Use more shallow data structures --- lib/Zonemaster/Backend/TestAgent.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 75f2b099d..7b11e51b0 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -44,7 +44,7 @@ sub new { die "default profile cannot be private" if ( $profile eq 'default' && $all_profiles{$profile}{type} eq 'private' ); if ( -e $all_profiles{$profile}{profile_file_name} ) { my $json = read_file( $all_profiles{$profile}{profile_file_name}, err_mode => 'croak' ); - $self->{profiles}{$profile}{zm_profile} = Zonemaster::Engine::Profile->from_json( $json ); + $self->{profiles}{$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"; @@ -138,9 +138,9 @@ 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}) { + if ( defined $self->{profiles}{ $params->{profile} } ) { my $profile = Zonemaster::Engine::Profile->default; - $profile->merge( $self->{profiles}->{$params->{profile}}->{zm_profile} ); + $profile->merge( $self->{profiles}{$params->{profile}} ); Zonemaster::Engine::Profile->effective->merge( $profile ); } else { From 2578d9fdc81cd45d2bdc3fceeb037176058baee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 16:18:19 +0200 Subject: [PATCH 066/424] Rename variables --- lib/Zonemaster/Backend/TestAgent.pm | 36 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 7b11e51b0..384d50346 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -35,19 +35,21 @@ sub new { $dbtype = $config->DB_engine; } - my $backend_module = "Zonemaster::Backend::DB::" . $dbtype; - eval "require $backend_module"; - $self->{db} = $backend_module->new( { config => $config } ); + my $db_module = "Zonemaster::Backend::DB::" . $dbtype; + eval "require $db_module"; + $self->{_db} = $db_module->new( { config => $config } ); my %all_profiles = %{ $config->ReadProfilesInfo() }; - foreach my $profile ( keys %all_profiles ) { - die "default profile cannot be private" if ( $profile eq 'default' && $all_profiles{$profile}{type} eq 'private' ); - if ( -e $all_profiles{$profile}{profile_file_name} ) { - my $json = read_file( $all_profiles{$profile}{profile_file_name}, err_mode => 'croak' ); - $self->{profiles}{$profile} = Zonemaster::Engine::Profile->from_json( $json ); + foreach my $name ( keys %all_profiles ) { + my $path = $all_profiles{$name}{profile_file_name}; + + die "default profile cannot be private" if ( $name eq 'default' && $all_profiles{$name}{type} eq 'private' ); + if ( -e $path ) { + my $json = read_file( $path, err_mode => 'croak' ); + $self->{_profiles}{$name} = 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"; + elsif ( $name ne 'default' ) { + die "the profile definition json file of the profile [$name] defined in the backend config file can't be read"; } } @@ -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,9 +140,9 @@ 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} } ) { + if ( defined $self->{_profiles}{ $params->{profile} } ) { my $profile = Zonemaster::Engine::Profile->default; - $profile->merge( $self->{profiles}{$params->{profile}} ); + $profile->merge( $self->{_profiles}{$params->{profile}} ); Zonemaster::Engine::Profile->effective->merge( $profile ); } else { @@ -170,9 +172,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 From 4326938ce7c0e8f3595b828ef94ed1f2ff32f3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 17:19:04 +0200 Subject: [PATCH 067/424] Perform fewer profile merging operations --- lib/Zonemaster/Backend/TestAgent.pm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 384d50346..221eae5d6 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -44,13 +44,15 @@ sub new { my $path = $all_profiles{$name}{profile_file_name}; die "default profile cannot be private" if ( $name eq 'default' && $all_profiles{$name}{type} eq 'private' ); + my $profile = Zonemaster::Engine::Profile->default; if ( -e $path ) { my $json = read_file( $path, err_mode => 'croak' ); - $self->{_profiles}{$name} = Zonemaster::Engine::Profile->from_json( $json ); + $profile->merge( Zonemaster::Engine::Profile->from_json( $json ) ); } elsif ( $name ne 'default' ) { die "the profile definition json file of the profile [$name] defined in the backend config file can't be read"; } + $self->{_profiles}{$name} = $profile; } bless( $self, $class ); @@ -141,12 +143,10 @@ sub run { if ( $params->{profile} ) { $params->{profile} = lc($params->{profile}); if ( defined $self->{_profiles}{ $params->{profile} } ) { - my $profile = Zonemaster::Engine::Profile->default; - $profile->merge( $self->{_profiles}{$params->{profile}} ); - Zonemaster::Engine::Profile->effective->merge( $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"; } } From 1c0c57abb200f074f6960fa75c6c9d142ef3f83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 15:23:09 +0200 Subject: [PATCH 068/424] Provide more error context --- lib/Zonemaster/Backend/TestAgent.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 221eae5d6..29b86b4f7 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -45,12 +45,12 @@ sub new { die "default profile cannot be private" if ( $name eq 'default' && $all_profiles{$name}{type} eq 'private' ); my $profile = Zonemaster::Engine::Profile->default; - if ( -e $path ) { - my $json = read_file( $path, err_mode => 'croak' ); - $profile->merge( Zonemaster::Engine::Profile->from_json( $json ) ); - } - elsif ( $name ne 'default' ) { - die "the profile definition json file of the profile [$name] defined in the backend config file can't be read"; + if ( $path ne "" ) { + 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': $@"; + $profile->merge( $named_profile ); } $self->{_profiles}{$name} = $profile; } From 289629975eb1c465ba71fb57b9d2bde3e28cbbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 22 Jun 2021 19:58:01 +0200 Subject: [PATCH 069/424] Log startup failures --- script/zonemaster_backend_testagent | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index f3fa56d73..7d5ff1dd3 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -163,20 +163,20 @@ 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(); @@ -189,6 +189,7 @@ eval { $log->debug("Completed pre-flight check"); }; if ( $@ ) { + $log->critical( "Aborting startup: $@" ); print STDERR "Aborting startup: $@"; exit 1; } From 00d022f37982c32a7a4a5b4d09dc62ba12cdb12a Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 28 Jun 2021 11:06:19 +0200 Subject: [PATCH 070/424] Deprecate the use of language tags with country codes --- docs/API.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/API.md b/docs/API.md index fd0d1def2..0b36e4935 100644 --- a/docs/API.md +++ b/docs/API.md @@ -160,8 +160,9 @@ This parameter is a string that are an IPv4 or IPv6. It's validated with the fol Basic data type: string -A string of A-Z, a-z and underscores matching the regular expression -`/^[a-z]{2}(_[A-Z]{2})?$/`. +A string of A-Z, a-z and underscores matching one of the regular expressions +`/^[a-z]{2}$/` or `/^[a-z]{2}_[A-Z]{2}$/`. The first format is the preferred +format, and the second format is *deprecated*. The set of valid *language tags* is further constrained by the [LANGUAGE.locale] property. @@ -174,6 +175,8 @@ The set of valid *language tags* is further constrained by the 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 @@ -183,12 +186,15 @@ 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*: -* `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. + +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 @@ -391,6 +397,8 @@ 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 From b3a51cb1e683747f2a0e72ba75ba8711bdd41f1f Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 28 Jun 2021 11:06:30 +0200 Subject: [PATCH 071/424] Deprecate the use of language tags with country codes --- docs/Configuration.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 4194f000b..74f97e54f 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -205,22 +205,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` it in the +configuration, but none starting with language code for English ("en"). #### 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 +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: @@ -233,9 +233,15 @@ locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE 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 +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 From eebb966a4eed9d7831cff2eaca409033addfc58c Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 28 Jun 2021 20:33:05 +0200 Subject: [PATCH 072/424] Editorial update --- docs/API.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 0b36e4935..18f3673a1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -160,9 +160,8 @@ This parameter is a string that are an IPv4 or IPv6. It's validated with the fol Basic data type: string -A string of A-Z, a-z and underscores matching one of the regular expressions -`/^[a-z]{2}$/` or `/^[a-z]{2}_[A-Z]{2}$/`. The first format is the preferred -format, and the second format is *deprecated*. +A string matching the regular expression `/^[a-z]{2}$/` (preferred format) or a string +matching the regular expressions `/^[a-z]{2}_[A-Z]{2}$/` (*deprecated* format). The set of valid *language tags* is further constrained by the [LANGUAGE.locale] property. From c185a3fac0ddc460880b91b00f2121d5739969ef Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 28 Jun 2021 20:33:30 +0200 Subject: [PATCH 073/424] Fixes typo --- docs/Configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 74f97e54f..62f7a2868 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -205,7 +205,7 @@ 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 including some `locale tag` it in the +from being allowed by RPC-API by including some `locale tag` in the configuration, but none starting with language code for English ("en"). #### Out-of-the-box support From 6f19f6557670ca555abe2e95d5033578a5b1deaa Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 29 Jun 2021 10:45:31 +0200 Subject: [PATCH 074/424] Editorial update --- docs/API.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 18f3673a1..e180aae07 100644 --- a/docs/API.md +++ b/docs/API.md @@ -160,8 +160,9 @@ This parameter is a string that are an IPv4 or IPv6. It's validated with the fol Basic data type: string -A string matching the regular expression `/^[a-z]{2}$/` (preferred format) or a string -matching the regular expressions `/^[a-z]{2}_[A-Z]{2}$/` (*deprecated* format). +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. From 75db9a7e387430eeca899eabbacdfa59514fa21e Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 29 Jun 2021 10:46:21 +0200 Subject: [PATCH 075/424] Deprecates empty LANGUAGE.locale --- docs/Configuration.md | 3 +++ lib/Zonemaster/Backend/Config.pm | 23 ++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 62f7a2868..a2450d0dc 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -181,6 +181,9 @@ The LANGUAGE section has one key, `locale`. ### locale +Leaving the `locale` key empty or absent is *deprecated*. Always configure it +with supported `locale tags`. + A space separated list of `locale tags` where each tag matches the regular expression `/^[a-z]{2}_[A-Z]{2}$/`. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index efccdefaa..63f009cd8 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -254,15 +254,20 @@ sub parse { $obj->_set_ZONEMASTER_age_reuse_previous_test( $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; + { + $obj->{_LANGUAGE_locale} = {}; + my $values = $get_and_clear->( 'LANGUAGE', 'locale' ); + unless ($values) { + push @warnings, "Use of empty LANGUAGE.locale propery is deprecated."; + $values = 'en_US'; + } + for my $locale_tag ( split /\s+/, $values ) { + $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; + } } $obj->{_public_profiles} = { From d077703a0c8208fb44509a03bc41a4736e2ee3a6 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 29 Jun 2021 11:34:44 +0200 Subject: [PATCH 076/424] Editorial update --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index e180aae07..6b72d0444 100644 --- a/docs/API.md +++ b/docs/API.md @@ -162,7 +162,7 @@ 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. +* `/^[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. From ea0c3a3ead5913bc4ad2668620155bd92de253c6 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 29 Jun 2021 16:45:24 +0200 Subject: [PATCH 077/424] Empty From 92dac275c8f9a3a697cf8a1726916a20d7c68436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 14:22:24 +0200 Subject: [PATCH 078/424] Inline ListLanguageTags() --- lib/Zonemaster/Backend/Config.pm | 25 ------------------------- lib/Zonemaster/Backend/RPCAPI.pm | 9 ++++++--- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 63f009cd8..000110ab6 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -595,31 +595,6 @@ sub Language_Locale_hash { 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) = @_; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 7ef40ca9c..117fa527b 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -120,9 +120,12 @@ $json_schemas{get_language_tags} = joi->object->strict; sub get_language_tags { my ( $self ) = @_; - my @lang = $self->{config}->ListLanguageTags(); - - return \@lang; + my %locale_tags = $self->{config}->Language_Locale_hash(); + my @langtags; + foreach my $key (keys %locale_tags) { + push @langtags, $key unless $locale_tags{$key} eq 'NOT-UNIQUE'; + } + return \@langtags; } $json_schemas{get_host_by_name} = joi->object->strict->props( From 2795f1460a7c58affa4fb0e6166837eb7dc2fc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 18 Jun 2021 14:14:38 +0200 Subject: [PATCH 079/424] Change internal representation --- lib/Zonemaster/Backend/Config.pm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 000110ab6..74030a3a9 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -261,12 +261,16 @@ sub parse { push @warnings, "Use of empty LANGUAGE.locale propery is deprecated."; $values = 'en_US'; } - for my $locale_tag ( split /\s+/, $values ) { + for my $locale_tag ( split /\s+/, $values // '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; + or die "Illegal locale tag in LANGUAGE.locale: $locale_tag\n"; + + my $lang_code = $locale_tag =~ s/_..$//r; + + !exists $obj->{_LANGUAGE_locale}{$lang_code}{$locale_tag} + or die "Repeated locale tag in LANGUAGE.locale: $locale_tag\n"; + + $obj->{_LANGUAGE_locale}{$lang_code}{$locale_tag} = 1; } } @@ -577,7 +581,7 @@ 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 @localetags = map { keys %{$_} } values %{ $self->{_LANGUAGE_locale} }; my %locale; foreach my $la (@localetags) { (my $a) = split (/_/,$la); # $a is the language code only From 8e9f1e03c58f8274781fbbdf4a7079e807ea36b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 9 Apr 2021 22:20:34 +0200 Subject: [PATCH 080/424] Add accessor for LANGUAGE.locale --- lib/Zonemaster/Backend/Config.pm | 80 ++++++++++++++++++++++------- lib/Zonemaster/Backend/Validator.pm | 14 +++++ t/config.t | 63 +++++++++++++---------- t/validator.t | 7 +++ 4 files changed, 118 insertions(+), 46 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 74030a3a9..8c1727701 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -130,6 +130,7 @@ sub parse { $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->_add_LANGUAGE_locale( 'en_US' ); # Assign property values (part 1/2) if ( defined( my $value = $get_and_clear->( 'DB', 'engine' ) ) ) { @@ -184,6 +185,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 propery is deprecated. Remove LANGUAGE.locale entry or specify LANUGAGE.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."; @@ -253,24 +259,12 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'ZONEMASTER', 'age_reuse_previous_test' ) ) ) { $obj->_set_ZONEMASTER_age_reuse_previous_test( $value ); } - - { - $obj->{_LANGUAGE_locale} = {}; - my $values = $get_and_clear->( 'LANGUAGE', 'locale' ); - unless ($values) { - push @warnings, "Use of empty LANGUAGE.locale propery is deprecated."; - $values = 'en_US'; - } - for my $locale_tag ( split /\s+/, $values // 'en_US' ) { - $locale_tag =~ /^[a-z]{2}_[A-Z]{2}$/ - or die "Illegal locale tag in LANGUAGE.locale: $locale_tag\n"; - - my $lang_code = $locale_tag =~ s/_..$//r; - - !exists $obj->{_LANGUAGE_locale}{$lang_code}{$locale_tag} - or die "Repeated locale tag in LANGUAGE.locale: $locale_tag\n"; - - $obj->{_LANGUAGE_locale}{$lang_code}{$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 ); + } } } @@ -469,6 +463,28 @@ 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 ZONEMASTER_max_zonemaster_execution_time Get the value of L. @@ -530,6 +546,7 @@ sub POSTGRESQL_user { return $_[0]->{_POSTGR 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 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}; } @@ -581,7 +598,7 @@ sub Language_Locale_hash { # There is one special value to capture ambiguous (and therefore # not permitted) translation language tags. my ($self) = @_; - my @localetags = map { keys %{$_} } values %{ $self->{_LANGUAGE_locale} }; + my @localetags = map { keys %{$_} } values %{ { $self->LANGUAGE_locale } }; my %locale; foreach my $la (@localetags) { (my $a) = split (/_/,$la); # $a is the language code only @@ -753,6 +770,31 @@ sub new_PM { return $pm; } +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; +} + # Create a setter method with a given name using the given field and validator sub _create_setter { my ( $setter, $field, $validate ) = @_; diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index ad7071221..2d743eae6 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -20,6 +20,7 @@ our @EXPORT_OK = qw( untaint_ipv6_address untaint_host untaint_ldh_domain + untaint_locale_tag untaint_mariadb_database untaint_mariadb_user untaint_non_negative_int @@ -39,6 +40,7 @@ our %EXPORT_TAGS = ( untaint_ipv6_address untaint_host untaint_ldh_domain + untaint_locale_tag untaint_mariadb_database untaint_mariadb_user untaint_non_negative_int @@ -66,6 +68,7 @@ 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 @@ -261,6 +264,17 @@ sub untaint_ldh_domain { 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 ); diff --git a/t/config.t b/t/config.t index 7f9087309..8392c9b31 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 @@ -39,6 +40,9 @@ subtest 'Everything but NoWarnings' => sub { [SQLITE] database_file = /var/db/zonemaster.sqlite + [LANGUAGE] + locale = sv_FI + [ZONEMASTER] max_zonemaster_execution_time = 1200 number_of_processes_for_frontend_testing = 30 @@ -49,25 +53,26 @@ subtest 'Everything but NoWarnings' => sub { }; 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_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'; - 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'; + 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'; }; lives_and { @@ -80,14 +85,15 @@ 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->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'; - is $config->MYSQL_port, 3306, 'default: MYSQL.port'; - is $config->POSTGRESQL_port, 5432, 'default: POSTGRESQL.port'; + 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'; + 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 { @@ -153,6 +159,8 @@ subtest 'Everything but NoWarnings' => sub { 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 @@ -160,6 +168,7 @@ 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'; }; diff --git a/t/validator.t b/t/validator.t index ac523f9aa..068f21b6d 100644 --- a/t/validator.t +++ b/t/validator.t @@ -81,6 +81,13 @@ subtest 'Everything but NoWarnings' => sub { 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'; From 8bea11745d2cfd6e4ac16e6808a0f8c98aeda971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Apr 2021 14:32:22 +0200 Subject: [PATCH 081/424] Use LANGUAGE_locale directly --- lib/Zonemaster/Backend/Config.pm | 41 ---------------------------- lib/Zonemaster/Backend/RPCAPI.pm | 47 ++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 56 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 8c1727701..f6cecf1c3 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -576,47 +576,6 @@ UNITCHECK { _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_strictly_positive_int ); } -=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 = map { keys %{$_} } values %{ { $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; -} - - sub ReadProfilesInfo { my ($self) = @_; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 117fa527b..206d1b0a8 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -120,12 +120,18 @@ $json_schemas{get_language_tags} = joi->object->strict; sub get_language_tags { my ( $self ) = @_; - my %locale_tags = $self->{config}->Language_Locale_hash(); - my @langtags; - foreach my $key (keys %locale_tags) { - push @langtags, $key unless $locale_tags{$key} eq 'NOT-UNIQUE'; + my %locales = $self->{config}->LANGUAGE_locale; + + my @lang_tags; + 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; } - return \@langtags; + + return \@lang_tags; } $json_schemas{get_host_by_name} = joi->object->strict->props( @@ -443,22 +449,33 @@ $json_schemas{get_test_results} = joi->object->strict->props( sub get_test_results { my ( $self, $params ) = @_; - my $result; - my $translator; - $translator = Zonemaster::Backend::Translator->new; + my $language = $params->{language}; + + my %locales = $self->{config}->LANGUAGE_locale; - 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 $locale_tag; + if ( length $language == 2 ) { + if ( !exists $locales{$language} ) { + die "Undefined language string: '$language'\n"; } + elsif ( scalar keys %{ $locales{$language} } > 1 ) { + die "Language string not unique: '$language'\n"; + } + ( $locale_tag ) = keys %{ $locales{$language} }; } else { - die "Undefined language string: '$params->{language}'\n"; + if ( !exists $locales{substr $language, 0, 2}{$language} ) { + die "Undefined language string: '$language'\n"; + } + $locale_tag = $language; } + my $result; + my $translator; + $translator = Zonemaster::Backend::Translator->new; + my $previous_locale = $translator->locale; - $translator->locale( $locale{$params->{language}} ); + $translator->locale( $locale_tag ); eval { $translator->data } if $translator; # Provoke lazy loading of translation data @@ -479,7 +496,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, $locale_tag ) . "\n"; $res->{message} =~ s/,/, /isg; $res->{message} =~ s/;/; /isg; $res->{level} = $test_res->{level}; From f88489a631ec3bd840063b652439f5ba376f95e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 1 Jul 2021 10:28:39 +0200 Subject: [PATCH 082/424] Add some headings to unit test --- t/config.t | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/config.t b/t/config.t index 8392c9b31..145d3f39b 100644 --- a/t/config.t +++ b/t/config.t @@ -17,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 @@ -75,7 +75,7 @@ subtest 'Everything but NoWarnings' => sub { 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 @@ -96,7 +96,7 @@ subtest 'Everything but NoWarnings' => sub { 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 { my $text = q{ [DB] engine = MySQL @@ -116,7 +116,7 @@ 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 { my $text = q{ [DB] engine = PostgreSQL @@ -136,7 +136,7 @@ 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 { my $text = q{ [DB] engine = SQLite @@ -153,7 +153,7 @@ 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 { my $text = q{ [DB] engine = SQLite @@ -173,7 +173,7 @@ subtest 'Everything but NoWarnings' => sub { is $config->ZONEMASTER_number_of_processes_for_batch_testing, '22', 'fallback: ZONEMASTER.number_of_processes_for_batch_testing'; }; - lives_and { + subtest 'Warnings' => sub { my $text = q{ [DB] engine = MySQL From b6485f9999c4dfe1328a3bace2b1447daede68d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 1 Jul 2021 10:31:18 +0200 Subject: [PATCH 083/424] Clear log between tests --- t/config.t | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/config.t b/t/config.t index 145d3f39b..826c85f02 100644 --- a/t/config.t +++ b/t/config.t @@ -97,6 +97,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'Deprecated values and fallbacks when DB.engine = MySQL' => sub { + $log->clear(); my $text = q{ [DB] engine = MySQL @@ -117,6 +118,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'Deprecated values and fallbacks when DB.engine = PostgreSQL' => sub { + $log->clear(); my $text = q{ [DB] engine = PostgreSQL @@ -137,6 +139,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'Deprecated values and fallbacks when DB.engine = SQLite' => sub { + $log->clear(); my $text = q{ [DB] engine = SQLite @@ -154,6 +157,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'Deprecated values and fallbacks that are unconditional' => sub { + $log->clear(); my $text = q{ [DB] engine = SQLite @@ -174,6 +178,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest 'Warnings' => sub { + $log->clear(); my $text = q{ [DB] engine = MySQL From 519887e3ae0e7ba035915ac1a2f176b86946159c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 2 Jul 2021 11:26:19 +0200 Subject: [PATCH 084/424] Fix spelling and undeprecate absent LANGUAGE.locale --- docs/Configuration.md | 10 +++++----- lib/Zonemaster/Backend/Config.pm | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index a2450d0dc..7ff1955c4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -181,11 +181,11 @@ The LANGUAGE section has one key, `locale`. ### locale -Leaving the `locale` key empty or absent is *deprecated*. Always configure it -with supported `locale tags`. - -A space separated list of `locale tags` where each tag matches the regular -expression `/^[a-z]{2}_[A-Z]{2}$/`. +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`. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index f6cecf1c3..a313ed9e8 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -187,7 +187,7 @@ sub parse { } if ( defined( my $value = $ini->val( 'LANGUAGE', 'locale' ) ) ) { if ( $value eq "" ) { - push @warnings, "Use of empty LANGUAGE.locale propery is deprecated. Remove LANGUAGE.locale entry or specify LANUGAGE.locale = en_US instead."; + push @warnings, "Use of empty LANGUAGE.locale propery 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' ) ) ) { From 4e3cc710dd82145be546eda7cfdbe81bb20e811a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 2 Jul 2021 11:32:41 +0200 Subject: [PATCH 085/424] Spelling --- lib/Zonemaster/Backend/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index a313ed9e8..bb0cdc83e 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -187,7 +187,7 @@ sub parse { } if ( defined( my $value = $ini->val( 'LANGUAGE', 'locale' ) ) ) { if ( $value eq "" ) { - push @warnings, "Use of empty LANGUAGE.locale propery is deprecated. Remove the LANGUAGE.locale entry or specify LANGUAGE.locale = en_US instead."; + 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' ) ) ) { From f592c7403720d96c55fe945f244cfc3e3c328738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 28 Jun 2021 20:07:13 +0200 Subject: [PATCH 086/424] Fix connecting to MySQL over IPv6 --- lib/Zonemaster/Backend/DB/MySQL.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 86c387eec..2a71fe2fd 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -42,14 +42,14 @@ sub dbh { my $user = $self->config->MYSQL_user; my $password = $self->config->MYSQL_password; - my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; - $log->notice( "Connecting to MySQL: database=$database host=$host user=$user" ) if $log->is_notice; if ( untaint_ipv6_address( $host ) ) { $host = "[$host]"; } + my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; + $dbh = DBI->connect( $data_source_name, $user, From 03bebfa522aa78232c31b57cc8ddf544bf90c849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Sun, 27 Jun 2021 14:56:48 +0200 Subject: [PATCH 087/424] Clean up: DB.engine is already validated --- lib/Zonemaster/Backend/Config.pm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 955a8640e..5ac3497d8 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -620,8 +620,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. @@ -633,11 +631,7 @@ A configured L object. 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; From 831f3e64492745c1923a8d341969624aceda964e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 24 Jun 2021 17:17:15 +0200 Subject: [PATCH 088/424] Refactor: Introduce DB::get_db_class --- lib/Zonemaster/Backend/Config.pm | 8 +++----- lib/Zonemaster/Backend/DB.pm | 19 +++++++++++++++++++ lib/Zonemaster/Backend/RPCAPI.pm | 6 ++---- lib/Zonemaster/Backend/TestAgent.pm | 5 ++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 5ac3497d8..e6c64b368 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -13,6 +13,7 @@ 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}) { @@ -634,11 +635,8 @@ sub new_DB { my $dbtype = $self->DB_engine; # 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 $dbclass = Zonemaster::Backend::DB->get_db_class( $self->DB_engine ); + my $db = $dbclass->new( { config => $self } ); # Connect or die $db->dbh; diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 0e78e23d5..7c4a51de2 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -24,6 +24,25 @@ requires qw( user_exists_in_db ); +=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 user_exists { my ( $self, $user ) = @_; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 4a1f6d2a5..191455659 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -62,10 +62,8 @@ 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->new( { config => $self->{config} } ); }; if ($@) { handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 29b86b4f7..0e1ef24fb 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -35,9 +35,8 @@ sub new { $dbtype = $config->DB_engine; } - my $db_module = "Zonemaster::Backend::DB::" . $dbtype; - eval "require $db_module"; - $self->{_db} = $db_module->new( { config => $config } ); + my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); + $self->{_db} = $dbclass->new( { config => $config } ); my %all_profiles = %{ $config->ReadProfilesInfo() }; foreach my $name ( keys %all_profiles ) { From 0c518fc5004ea0095e7be59c6f9f94cfc7b2036d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 8 Jun 2021 14:02:34 +0200 Subject: [PATCH 089/424] Make DB adapters not access config after construction --- lib/Zonemaster/Backend/DB.pm | 19 +++++++++++-------- lib/Zonemaster/Backend/DB/MySQL.pm | 14 +++++++------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 14 +++++++------- lib/Zonemaster/Backend/DB/SQLite.pm | 14 +++++++------- script/zonemaster_backend_testagent | 9 ++++++--- 5 files changed, 38 insertions(+), 32 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 7c4a51de2..3bb6d8241 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -68,16 +68,15 @@ sub add_api_user { # 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 ] ); @@ -121,12 +120,16 @@ sub get_batch_job_result { } sub process_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; - my $sth1 = $self->select_unfinished_tests(); + my $sth1 = $self->select_unfinished_tests( # + $queue_label, + $test_run_timeout, + $test_run_max_retries, + ); while ( my $h = $sth1->fetchrow_hashref ) { - if ( $h->{nb_retries} < $self->config->ZONEMASTER_maximal_number_of_retries ) { + if ( $h->{nb_retries} < $test_run_max_retries ) { $self->schedule_for_retry($h->{hash_id}); } else { @@ -142,7 +145,7 @@ sub process_unfinished_tests { "level" => "CRITICAL", "module" => "BACKEND_TEST_AGENT", "tag" => "UNABLE_TO_FINISH_TEST", - "timestamp" => $self->config->ZONEMASTER_max_zonemaster_execution_time + "timestamp" => $test_run_timeout, }; $self->process_unfinished_tests_give_up($result, $h->{hash_id}); } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 2a71fe2fd..1e4494f56 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -346,9 +346,9 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " SELECT hash_id, results, nb_retries FROM test_results @@ -358,9 +358,9 @@ sub select_unfinished_tests { 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, + $test_run_max_retries, + $queue_label, ); return $sth; } @@ -373,8 +373,8 @@ sub select_unfinished_tests { 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, + $test_run_max_retries, ); return $sth; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index f94288406..8e0f4e165 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -318,9 +318,9 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " SELECT hash_id, results, nb_retries FROM test_results @@ -330,9 +330,9 @@ sub select_unfinished_tests { 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 ), + $test_run_max_retries, + $queue_label, ); return $sth; } @@ -345,8 +345,8 @@ sub select_unfinished_tests { 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 ), + $test_run_max_retries, ); return $sth; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 433db3da6..0d16130f8 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -373,9 +373,9 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self ) = @_; + my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; - if ( $self->config->ZONEMASTER_lock_on_queue ) { + if ( $queue_label ) { my $sth = $self->dbh->prepare( " SELECT hash_id, results, nb_retries FROM test_results @@ -385,9 +385,9 @@ sub select_unfinished_tests { 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 ), + $test_run_max_retries, + $queue_label, ); return $sth; } @@ -400,8 +400,8 @@ sub select_unfinished_tests { 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 ), + $test_run_max_retries, ); return $sth; } diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index 7d5ff1dd3..de8c20d30 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -131,9 +131,12 @@ sub main { $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(); - + 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, + $self->config->ZONEMASTER_maximal_number_of_retries, + ); if ( $id ) { $log->info( "Test found: $id" ); if ( $self->pm->start( $id ) == 0 ) { # Forks off child process From 0157e8e93f44cc94da42dee4fb3a957fade87ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 30 Jun 2021 10:39:03 +0200 Subject: [PATCH 090/424] Connect to database more uniformly --- lib/Zonemaster/Backend/DB.pm | 28 ++++++++++++++++++++++++- lib/Zonemaster/Backend/DB/MySQL.pm | 10 +-------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 9 +------- lib/Zonemaster/Backend/DB/SQLite.pm | 10 +++------ 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 3bb6d8241..32ba4bd03 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -7,7 +7,7 @@ use Moose::Role; use 5.14.2; use JSON::PP; -use Data::Dumper; +use Log::Any qw( $log ); requires qw( add_api_user_to_db @@ -152,6 +152,32 @@ sub process_unfinished_tests { } } +# 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, + } + ); + + $dbh->{AutoInactiveDestroy} = 1; + + return $dbh; +} no Moose::Role; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 1e4494f56..be14853ed 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -10,7 +10,6 @@ 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 ); @@ -42,25 +41,18 @@ sub dbh { 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; - if ( untaint_ipv6_address( $host ) ) { $host = "[$host]"; } my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; - $dbh = DBI->connect( + $dbh = $self->_new_dbh( $data_source_name, $user, $password, - { - RaiseError => 1, - AutoCommit => 1 - } ); - $dbh->{AutoInactiveDestroy} = 1; $self->dbhandle( $dbh ); return $dbh; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 8e0f4e165..e81158081 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -9,7 +9,6 @@ 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; @@ -43,18 +42,12 @@ sub dbh { my $data_source_name = "DBI:Pg:database=$database;host=$host;port=$port"; - $log->notice( "Connecting to PostgreSQL: database=$database host=$host user=$user" ) if $log->is_notice; - $dbh = DBI->connect( + $dbh = $self->_new_dbh( $data_source_name, $user, $password, - { - RaiseError => 1, - AutoCommit => 1 - } ); - $dbh->{AutoInactiveDestroy} = 1; $self->dbhandle( $dbh ); return $dbh; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 0d16130f8..9509bfdbf 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -33,14 +33,10 @@ sub BUILD { 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( + my $dbh = $self->_new_dbh( # "DBI:SQLite:dbname=$file", - undef, undef, - { - AutoCommit => 1, - RaiseError => 1, - } + '', + '', ); $self->dbh( $dbh ); From 22c6ad7365c352b2386b46833c0c739f16e703e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 30 Jun 2021 12:33:36 +0200 Subject: [PATCH 091/424] Make DB adapters not store Config --- lib/Zonemaster/Backend/DB/MySQL.pm | 61 +++++++++++++++++-------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 53 +++++++++++++++------ lib/Zonemaster/Backend/DB/SQLite.pm | 34 ++++++-------- 3 files changed, 95 insertions(+), 53 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index be14853ed..aeb077c78 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -11,14 +11,25 @@ use Digest::MD5 qw(md5_hex); use Encode; use JSON::PP; -use Zonemaster::Backend::Config; use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); with 'Zonemaster::Backend::DB'; -has 'config' => ( +has 'data_source_name' => ( is => 'ro', - isa => 'Zonemaster::Backend::Config', + isa => 'Str', + required => 1, +); + +has 'user' => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'password' => ( + is => 'ro', + isa => 'Str', required => 1, ); @@ -27,6 +38,32 @@ has 'dbhandle' => ( isa => 'DBI::db', ); +around BUILDARGS => sub { + my ( $orig, $class, $args ) = @_; + + my $config = $args->{config}; + + 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; + + if ( untaint_ipv6_address( $host ) ) { + $host = "[$host]"; + } + + my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; + + return $class->$orig( + { + data_source_name => $data_source_name, + user => $user, + password => $password, + } + ); +}; + sub dbh { my ( $self ) = @_; my $dbh = $self->dbhandle; @@ -35,22 +72,10 @@ sub dbh { return $dbh; } else { - my $database = $self->config->MYSQL_database; - my $host = $self->config->MYSQL_host; - my $port = $self->config->MYSQL_port; - my $user = $self->config->MYSQL_user; - my $password = $self->config->MYSQL_password; - - if ( untaint_ipv6_address( $host ) ) { - $host = "[$host]"; - } - - my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; - $dbh = $self->_new_dbh( - $data_source_name, - $user, - $password, + $self->data_source_name, + $self->user, + $self->password, ); $self->dbhandle( $dbh ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e81158081..73fc50188 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -11,13 +11,24 @@ use Encode; use JSON::PP; use Zonemaster::Backend::DB; -use Zonemaster::Backend::Config; with 'Zonemaster::Backend::DB'; -has 'config' => ( +has 'data_source_name' => ( is => 'ro', - isa => 'Zonemaster::Backend::Config', + isa => 'Str', + required => 1, +); + +has 'user' => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has 'password' => ( + is => 'ro', + isa => 'Str', required => 1, ); @@ -26,6 +37,28 @@ has 'dbhandle' => ( isa => 'DBI::db', ); +around BUILDARGS => sub { + my ( $orig, $class, $args ) = @_; + + my $config = $args->{config}; + + 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:database=$database;host=$host;port=$port"; + + return $class->$orig( + { + data_source_name => $data_source_name, + user => $user, + password => $password, + } + ); +}; + sub dbh { my ( $self ) = @_; my $dbh = $self->dbhandle; @@ -34,18 +67,10 @@ sub dbh { return $dbh; } else { - my $database = $self->config->POSTGRESQL_database; - my $host = $self->config->POSTGRESQL_host; - my $port = $self->config->POSTGRESQL_port; - my $user = $self->config->POSTGRESQL_user; - my $password = $self->config->POSTGRESQL_password; - - my $data_source_name = "DBI:Pg:database=$database;host=$host;port=$port"; - $dbh = $self->_new_dbh( - $data_source_name, - $user, - $password, + $self->data_source_name, + $self->user, + $self->password, ); $self->dbhandle( $dbh ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 9509bfdbf..9b442718f 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -12,38 +12,30 @@ use Encode; use JSON::PP; use Log::Any qw( $log ); -use Zonemaster::Backend::Config; - with 'Zonemaster::Backend::DB'; -has 'config' => ( - is => 'ro', - isa => 'Zonemaster::Backend::Config', - required => 1, -); - has 'dbh' => ( is => 'rw', isa => 'DBI::db', ); -sub BUILD { - my ( $self ) = @_; +around BUILDARGS => sub { + my ( $orig, $class, $args ) = @_; - if ( !defined $self->dbh ) { - my $file = $self->config->SQLITE_database_file; + my $config = $args->{config}; - my $dbh = $self->_new_dbh( # - "DBI:SQLite:dbname=$file", - '', - '', - ); + my $file = $config->SQLITE_database_file; - $self->dbh( $dbh ); - } + my $data_source_name = "DBI:SQLite:dbname=$file"; - return $self; -} + my $dbh = $args->{dbh} // $class->_new_dbh( $data_source_name, '', '' ); + + return $class->$orig( + { + dbh => $dbh, + } + ); +}; sub DEMOLISH { my ( $self ) = @_; From dc5b1cdad08fd19dfc0985285f6e0ddaa79508c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Sun, 27 Jun 2021 17:54:03 +0200 Subject: [PATCH 092/424] Make DB adapters connect early --- lib/Zonemaster/Backend/Config.pm | 3 --- lib/Zonemaster/Backend/DB/MySQL.pm | 12 ++++++++++-- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 12 ++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index e6c64b368..e24a8cd0b 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -638,9 +638,6 @@ sub new_DB { my $dbclass = Zonemaster::Backend::DB->get_db_class( $self->DB_engine ); my $db = $dbclass->new( { config => $self } ); - # Connect or die - $db->dbh; - return $db; } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index aeb077c78..d57563f56 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -34,8 +34,9 @@ has 'password' => ( ); has 'dbhandle' => ( - is => 'rw', - isa => 'DBI::db', + is => 'rw', + isa => 'DBI::db', + required => 1, ); around BUILDARGS => sub { @@ -55,11 +56,18 @@ around BUILDARGS => sub { my $data_source_name = "DBI:mysql:database=$database;host=$host;port=$port"; + my $dbh = $class->_new_dbh( + $data_source_name, + $user, + $password, + ); + return $class->$orig( { data_source_name => $data_source_name, user => $user, password => $password, + dbhandle => $dbh, } ); }; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 73fc50188..a41ce7774 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -33,8 +33,9 @@ has 'password' => ( ); has 'dbhandle' => ( - is => 'rw', - isa => 'DBI::db', + is => 'rw', + isa => 'DBI::db', + required => 1, ); around BUILDARGS => sub { @@ -50,11 +51,18 @@ around BUILDARGS => sub { my $data_source_name = "DBI:Pg:database=$database;host=$host;port=$port"; + my $dbh = $class->_new_dbh( + $data_source_name, + $user, + $password, + ); + return $class->$orig( { data_source_name => $data_source_name, user => $user, password => $password, + dbhandle => $dbh, } ); }; From aa28baf0fbf6dbde5225954c87ce296f792c304e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 8 Jun 2021 14:18:38 +0200 Subject: [PATCH 093/424] Make DB adapter constructors not require config --- lib/Zonemaster/Backend/Config.pm | 2 +- lib/Zonemaster/Backend/DB.pm | 1 + lib/Zonemaster/Backend/DB/MySQL.pm | 18 ++++++++++++----- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 18 ++++++++++++----- lib/Zonemaster/Backend/DB/SQLite.pm | 20 +++++++++++++------ lib/Zonemaster/Backend/RPCAPI.pm | 2 +- lib/Zonemaster/Backend/TestAgent.pm | 8 ++++---- script/create_db_mysql.pl | 2 +- script/create_db_postgresql_9.3.pl | 2 +- share/create_db_sqlite.pl | 2 +- ...h_mysql_db_zonemaster_backend_ver_1.0.3.pl | 2 +- ...h_mysql_db_zonemaster_backend_ver_5.0.0.pl | 2 +- ...h_mysql_db_zonemaster_backend_ver_5.0.2.pl | 2 +- ...tgresql_db_zonemaster_backend_ver_1.0.3.pl | 2 +- ...tgresql_db_zonemaster_backend_ver_5.0.0.pl | 2 +- 15 files changed, 55 insertions(+), 30 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index e24a8cd0b..d7634d9ab 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -636,7 +636,7 @@ sub new_DB { # Load and construct DB adapter my $dbclass = Zonemaster::Backend::DB->get_db_class( $self->DB_engine ); - my $db = $dbclass->new( { config => $self } ); + my $db = $dbclass->from_config( $self ); return $db; } diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 32ba4bd03..2ec652a80 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -14,6 +14,7 @@ requires qw( add_batch_job create_new_batch_job create_new_test + from_config get_test_history get_test_params process_unfinished_tests_give_up diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index d57563f56..553af8a54 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -39,10 +39,18 @@ has 'dbhandle' => ( required => 1, ); -around BUILDARGS => sub { - my ( $orig, $class, $args ) = @_; +=head1 CLASS METHODS - my $config = $args->{config}; +=head2 from_config + +Construct a new instance from a Zonemaster::Backend::Config. + + my $db = Zonemaster::Backend::DB::MySQL->from_config( $config ); + +=cut + +sub from_config { + my ( $class, $config ) = @_; my $database = $config->MYSQL_database; my $host = $config->MYSQL_host; @@ -62,7 +70,7 @@ around BUILDARGS => sub { $password, ); - return $class->$orig( + return $class->new( { data_source_name => $data_source_name, user => $user, @@ -70,7 +78,7 @@ around BUILDARGS => sub { dbhandle => $dbh, } ); -}; +} sub dbh { my ( $self ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index a41ce7774..dd1f046be 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -38,10 +38,18 @@ has 'dbhandle' => ( required => 1, ); -around BUILDARGS => sub { - my ( $orig, $class, $args ) = @_; +=head1 CLASS METHODS - my $config = $args->{config}; +=head2 from_config + +Construct a new instance from a Zonemaster::Backend::Config. + + my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); + +=cut + +sub from_config { + my ( $class, $config ) = @_; my $database = $config->POSTGRESQL_database; my $host = $config->POSTGRESQL_host; @@ -57,7 +65,7 @@ around BUILDARGS => sub { $password, ); - return $class->$orig( + return $class->new( { data_source_name => $data_source_name, user => $user, @@ -65,7 +73,7 @@ around BUILDARGS => sub { dbhandle => $dbh, } ); -}; +} sub dbh { my ( $self ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 9b442718f..9cc23461f 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -19,23 +19,31 @@ has 'dbh' => ( isa => 'DBI::db', ); -around BUILDARGS => sub { - my ( $orig, $class, $args ) = @_; +=head1 CLASS METHODS - my $config = $args->{config}; +=head2 from_config + +Construct a new instance from a Zonemaster::Backend::Config. + + my $db = Zonemaster::Backend::DB::SQLite->from_config( $config ); + +=cut + +sub from_config { + my ( $class, $config ) = @_; my $file = $config->SQLITE_database_file; my $data_source_name = "DBI:SQLite:dbname=$file"; - my $dbh = $args->{dbh} // $class->_new_dbh( $data_source_name, '', '' ); + my $dbh = $class->_new_dbh( $data_source_name, '', '' ); - return $class->$orig( + return $class->new( { dbh => $dbh, } ); -}; +} sub DEMOLISH { my ( $self ) = @_; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 191455659..b2eabff5d 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -63,7 +63,7 @@ sub _init_db { eval { my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); - $self->{db} = $dbclass->new( { config => $self->{config} } ); + $self->{db} = $dbclass->from_config( $self->{config} ); }; if ($@) { handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 0e1ef24fb..48c048f6a 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -36,22 +36,22 @@ sub new { } my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); - $self->{_db} = $dbclass->new( { config => $config } ); + $self->{_db} = $dbclass->from_config( $config ); my %all_profiles = %{ $config->ReadProfilesInfo() }; foreach my $name ( keys %all_profiles ) { my $path = $all_profiles{$name}{profile_file_name}; die "default profile cannot be private" if ( $name eq 'default' && $all_profiles{$name}{type} eq 'private' ); - my $profile = Zonemaster::Engine::Profile->default; + my $full_profile = Zonemaster::Engine::Profile->default; if ( $path ne "" ) { 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': $@"; - $profile->merge( $named_profile ); + $full_profile->merge( $named_profile ); } - $self->{_profiles}{$name} = $profile; + $self->{_profiles}{$name} = $full_profile; } bless( $self, $class ); diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index 63a230131..3c296e660 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -13,7 +13,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 create_db { diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 1d943eeae..0778252f6 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -13,7 +13,7 @@ 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 $dbh = Zonemaster::Backend::DB::PostgreSQL->from_config( $config )->dbh; my $db_user = $config->POSTGRESQL_user; sub create_db { diff --git a/share/create_db_sqlite.pl b/share/create_db_sqlite.pl index b84cfd73c..f578e1f94 100644 --- a/share/create_db_sqlite.pl +++ b/share/create_db_sqlite.pl @@ -13,5 +13,5 @@ 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 } ); +my $db = Zonemaster::Backend::DB::SQLite->from_config( $config ); $db->create_db(); diff --git a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl index 020d1cb71..1d1510d5f 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl @@ -13,7 +13,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.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl index 6ecf99359..842e27b5a 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl @@ -13,7 +13,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_mysql_db_zonemaster_backend_ver_5.0.2.pl index 2082f6e31..a44f1265c 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl @@ -13,7 +13,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_1.0.3.pl b/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl index 344287a69..1871017b2 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl @@ -13,7 +13,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_postgresql_db_zonemaster_backend_ver_5.0.0.pl index 98f70b108..cb42c2f76 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl @@ -13,7 +13,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 { From c7d3135cd512d1dddf99bc33dfc71f2798fe6b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 30 Jun 2021 12:56:36 +0200 Subject: [PATCH 094/424] Refactor --- lib/Zonemaster/Backend/Config.pm | 8 +++----- lib/Zonemaster/Backend/DB.pm | 1 - lib/Zonemaster/Backend/DB/MySQL.pm | 11 ++++------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 11 ++++------- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index d7634d9ab..1b301368a 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -630,12 +630,10 @@ A configured L object. =cut sub new_DB { - my ($self) = @_; - - my $dbtype = $self->DB_engine; + my ( $self ) = @_; - # Load and construct DB adapter - my $dbclass = Zonemaster::Backend::DB->get_db_class( $self->DB_engine ); + my $dbtype = $self->DB_engine; + my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); my $db = $dbclass->from_config( $self ); return $db; diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 2ec652a80..f27819798 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -74,7 +74,6 @@ sub get_test_request { my $result_id; my $dbh = $self->dbh; - my ( $id, $hash_id ); 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 ); diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 553af8a54..4dab77ebf 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -82,21 +82,18 @@ sub from_config { sub dbh { my ( $self ) = @_; - my $dbh = $self->dbhandle; - if ( $dbh and $dbh->ping ) { - return $dbh; - } - else { - $dbh = $self->_new_dbh( + if ( !$self->dbhandle->ping ) { + my $dbh = $self->_new_dbh( # $self->data_source_name, $self->user, $self->password, ); $self->dbhandle( $dbh ); - return $dbh; } + + return $self->dbhandle; } sub user_exists_in_db { diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index dd1f046be..7ce2bb15c 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -77,21 +77,18 @@ sub from_config { sub dbh { my ( $self ) = @_; - my $dbh = $self->dbhandle; - if ( $dbh and $dbh->ping ) { - return $dbh; - } - else { - $dbh = $self->_new_dbh( + if ( !$self->dbhandle->ping ) { + my $dbh = $self->_new_dbh( # $self->data_source_name, $self->user, $self->password, ); $self->dbhandle( $dbh ); - return $dbh; } + + return $self->dbhandle; } sub user_exists_in_db { From 6676feeca74a65367aec706c5e3f214002b2944b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 30 Jun 2021 16:40:26 +0200 Subject: [PATCH 095/424] Use correct syntax in PostgreSQL DSN --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 7ce2bb15c..0000b5941 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -57,7 +57,7 @@ sub from_config { my $user = $config->POSTGRESQL_user; my $password = $config->POSTGRESQL_password; - my $data_source_name = "DBI:Pg:database=$database;host=$host;port=$port"; + my $data_source_name = "DBI:Pg:dbname=$database;host=$host;port=$port"; my $dbh = $class->_new_dbh( $data_source_name, From 500ffb1acb5bd8aac4bb9a386dab4536cf1a7c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 5 Jul 2021 10:23:45 +0200 Subject: [PATCH 096/424] Make validate_syntax.t not depend on DBD::mysql --- t/test_validate_syntax.t | 42 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 8bb2b4f13..95e19b2bb 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -8,32 +8,30 @@ use Test::NoWarnings; use Encode; use File::ShareDir qw[dist_file]; +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, + } +); - # Require Zonemaster::Backend::RPCAPI.pm test - use_ok( 'Zonemaster::Backend::RPCAPI' ); - - # 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' ); +subtest 'Everything but NoWarnings' => sub { + + my $can_use_threads = eval 'use threads; 1'; my $frontend_params = { ipv4 => 1, From 6723bc08c61ff4dbf6196cba497706392483c142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 5 Jul 2021 11:04:13 +0200 Subject: [PATCH 097/424] Rename $queue -> $queue_label --- lib/Zonemaster/Backend/DB/MySQL.pm | 12 ++++++------ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 12 ++++++------ lib/Zonemaster/Backend/DB/SQLite.pm | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 4dab77ebf..f51b46b9b 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -163,8 +163,8 @@ sub create_new_test { my $test_params_deterministic_hash = md5_hex( $encoded_params ); my $result_id; - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + my $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; eval { $dbh->do( q[LOCK TABLES test_results WRITE] ); @@ -187,7 +187,7 @@ sub create_new_test { undef, $batch_id, $priority, - $queue, + $queue_label, $test_params_deterministic_hash, $encoded_params, $test_params->{domain}, @@ -341,8 +341,8 @@ sub add_batch_job { $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" );}; @@ -357,7 +357,7 @@ sub add_batch_job { 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 ); + $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $test_params_deterministic_hash, $encoded_params ); } $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)" ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 0000b5941..d2528c203 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -173,8 +173,8 @@ sub create_new_test { 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 $priority = $test_params->{priority}; + my $queue_label = $test_params->{queue}; my $sth = $dbh->prepare( " INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params) @@ -187,7 +187,7 @@ sub create_new_test { my $nb_inserted = $sth->execute( # $batch_id, $priority, - $queue, + $queue_label, $test_params_deterministic_hash, $encoded_params, $test_params_deterministic_hash, @@ -312,8 +312,8 @@ sub add_batch_job { 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" ); @@ -329,7 +329,7 @@ sub add_batch_job { 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"); + $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$test_params_deterministic_hash\t$encoded_params\n"); } $dbh->pg_putcopyend(); $dbh->do( "ALTER TABLE test_results ADD PRIMARY KEY (id)" ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 9cc23461f..cf460195c 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -178,8 +178,8 @@ sub create_new_test { my $test_params_deterministic_hash = md5_hex( $encoded_params ); my $result_id; - my $priority = $test_params->{priority}; - my $queue = $test_params->{queue}; + my $priority = $test_params->{priority}; + my $queue_label = $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. @@ -208,7 +208,7 @@ sub create_new_test { $hash_id, $batch_id, $priority, - $queue, + $queue_label, $test_params_deterministic_hash, $encoded_params, $test_params->{domain}, @@ -334,8 +334,8 @@ sub add_batch_job { $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 " );}; @@ -350,7 +350,7 @@ sub add_batch_job { 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 ); + $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $test_params_deterministic_hash, $encoded_params ); } $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)" ); From c67084961b34fd3bf5fbc0b79bc6faabc3eaebc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Tue, 6 Jul 2021 11:13:42 +0200 Subject: [PATCH 098/424] Remove workaround for bug in incompatible version of Engine --- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- lib/Zonemaster/Backend/Translator.pm | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 4a1f6d2a5..1ac33ff69 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -493,7 +493,7 @@ sub get_test_results { } $res->{module} = $test_res->{module}; - $res->{message} = $translator->translate_tag( $test_res, $locale_tag ) . "\n"; + $res->{message} = $translator->translate_tag( $test_res ) . "\n"; $res->{message} =~ s/,/, /isg; $res->{message} =~ s/;/; /isg; $res->{level} = $test_res->{level}; 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 ); From a3cf94c8809488ce77767683095258a8f83bb312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 5 Jul 2021 18:21:15 +0200 Subject: [PATCH 099/424] Fix Engine::Translator->locale() usage --- lib/Zonemaster/Backend/RPCAPI.pm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 1ac33ff69..8c524c8fa 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -5,13 +5,14 @@ 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; # Zonemaster Modules use Zonemaster::LDNS; @@ -450,7 +451,7 @@ sub get_test_results { my %locales = $self->{config}->LANGUAGE_locale; - my $locale_tag; + my $locale; if ( length $language == 2 ) { if ( !exists $locales{$language} ) { die "Undefined language string: '$language'\n"; @@ -458,21 +459,24 @@ sub get_test_results { elsif ( scalar keys %{ $locales{$language} } > 1 ) { die "Language string not unique: '$language'\n"; } - ( $locale_tag ) = keys %{ $locales{$language} }; + ( $locale ) = keys %{ $locales{$language} }; } else { if ( !exists $locales{substr $language, 0, 2}{$language} ) { die "Undefined language string: '$language'\n"; } - $locale_tag = $language; + $locale = $language; } + $locale .= '.UTF-8'; my $result; my $translator; $translator = Zonemaster::Backend::Translator->new; my $previous_locale = $translator->locale; - $translator->locale( $locale_tag ); + if ( !$translator->locale( $locale ) ) { + handle_exception( 'get_test_results', "Failed to set locale: $locale", '017' ); + } eval { $translator->data } if $translator; # Provoke lazy loading of translation data From 4b26ccd731b337c45dd36717a8402b8ef7aed92d Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 16 Jun 2021 18:06:46 +0200 Subject: [PATCH 100/424] Remove trailing spaces and update spaces to tabs --- lib/Zonemaster/Backend/DB.pm | 60 ++++++++++++------------- lib/Zonemaster/Backend/DB/MySQL.pm | 28 ++++++------ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 34 +++++++------- lib/Zonemaster/Backend/DB/SQLite.pm | 26 +++++------ 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index f27819798..b7b584c52 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -73,7 +73,7 @@ sub get_test_request { my $result_id; my $dbh = $self->dbh; - + my ( $id, $hash_id ); 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 ); @@ -81,7 +81,7 @@ sub get_test_request { 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; @@ -91,43 +91,43 @@ sub get_test_request { # 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, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; - + my $sth1 = $self->select_unfinished_tests( # $queue_label, $test_run_timeout, $test_run_max_retries, ); - + while ( my $h = $sth1->fetchrow_hashref ) { if ( $h->{nb_retries} < $test_run_max_retries ) { $self->schedule_for_retry($h->{hash_id}); diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index f51b46b9b..418e2d547 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -131,15 +131,15 @@ 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 + 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 + WHERE test_results.progress<>100 LIMIT 1 ", undef, $username ); @@ -193,10 +193,10 @@ sub create_new_test { $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; } }; @@ -232,9 +232,9 @@ sub get_test_params { 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 ); } @@ -350,7 +350,7 @@ sub add_batch_job { 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 (?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -364,7 +364,7 @@ sub add_batch_job { $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; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index d2528c203..187b664da 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -132,7 +132,7 @@ 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; @@ -143,14 +143,14 @@ sub create_new_batch_job { 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 + 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 ); @@ -225,8 +225,8 @@ sub test_results { 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}) ); @@ -234,7 +234,7 @@ sub test_results { else { $result->{params} = decode_json( $result->{params} ); } - + if (utf8::is_utf8($result->{results} ) ) { $result->{results} = decode_json( encode_utf8($result->{results}) ); } @@ -261,7 +261,7 @@ sub get_test_history { 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, @@ -269,8 +269,8 @@ sub get_test_history { hash_id, 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 params->>'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; @@ -322,7 +322,7 @@ sub add_batch_job { $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" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -338,7 +338,7 @@ sub add_batch_job { $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->commit(); } else { diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index cf460195c..025b341ab 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -139,7 +139,7 @@ sub user_authorized { my ( $id ) = $self->dbh->selectrow_array( q[SELECT id FROM users WHERE username = ? AND api_key = ?], undef, $user, $api_key ); - + return $id; } @@ -147,14 +147,14 @@ 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 + 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 " ); @@ -247,7 +247,7 @@ sub get_test_params { eval { $result = decode_json( $params_json ); }; - + $log->warn( "decoding of params_json failed (test_id: [$test_id]):".Dumper($params_json) ) if $@; return $result; @@ -289,7 +289,7 @@ sub get_test_history { hash_id, creation_time, params, - results + results FROM test_results WHERE @@ -343,7 +343,7 @@ sub add_batch_job { 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 (?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -357,7 +357,7 @@ sub add_batch_job { $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; } From 13bada8aae6e8915dac5cf2d57a0588ea0965a63 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 28 Jun 2021 17:22:54 +0200 Subject: [PATCH 101/424] Remove deprecated parameter --- docs/API.md | 4 ++-- lib/Zonemaster/Backend/RPCAPI.pm | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/API.md b/docs/API.md index 6b72d0444..60541c1f6 100644 --- a/docs/API.md +++ b/docs/API.md @@ -644,7 +644,7 @@ An object with the following properties: * `"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. +* `"config"`: **No longer supported**. Use `"profile"` instead. * `"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`) @@ -1046,7 +1046,7 @@ 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. +* `"config"`: **No longer supported**. Use `"profile"` instead. * `"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: `[]`) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index b2eabff5d..6d18d1b38 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -382,11 +382,6 @@ sub start_domain_test { 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; From 4630899a73655811e902558281cdf5b5a76ceef9 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 6 Jul 2021 13:49:37 +0200 Subject: [PATCH 102/424] Corrects parameters for "get_test_history" --- script/zmb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/script/zmb b/script/zmb index 15afd4f07..cec40d217 100755 --- a/script/zmb +++ b/script/zmb @@ -340,7 +340,7 @@ sub cmd_get_test_results { Options: --domain DOMAIN_NAME - --nameserver true|false|null + --filter all|delegated|undelegated --offset COUNT --limit COUNT @@ -348,7 +348,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; @@ -356,7 +356,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 ); @@ -367,8 +367,11 @@ sub cmd_get_test_history { }, ); - if ( $opt_nameserver ) { - $params{frontend_params}{nameservers} = json_tern( $opt_nameserver ); + if ( $opt_filter ) { + unless ( $opt_filer =~ /^(?:all|delegated|undelegated)$/ ) { + die 'Illegal filter value. Expects "all", "delegated" or "undelegated" '; + } + $params{filter} = $opt_filter; } if ( defined $opt_offset ) { From f7bb4bb4fb61131c196c418263720e97bc040813 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 6 Jul 2021 13:51:11 +0200 Subject: [PATCH 103/424] Clarifies cryptic error message --- script/zmb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/zmb b/script/zmb index cec40d217..59723d82e 100755 --- a/script/zmb +++ b/script/zmb @@ -464,7 +464,7 @@ sub json_tern { return undef; } else { - die "unknown ternary value"; + die 'Illegal value. Expects "true", "false" or "null" '; } } From 62038f556b80ff71108f5002438ad474e1266a3f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 23 Jun 2021 13:03:44 +0200 Subject: [PATCH 104/424] Rename variable "deterministic hash" into "fingerprint" --- lib/Zonemaster/Backend/DB/MySQL.pm | 19 ++++++++++--------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 16 ++++++++-------- lib/Zonemaster/Backend/DB/SQLite.pm | 16 ++++++++-------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 418e2d547..872ce677c 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -158,9 +158,9 @@ sub create_new_test { 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 $js = JSON::PP->new->canonical; + my $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( $encoded_params ); my $result_id; my $priority = $test_params->{priority}; @@ -172,7 +172,7 @@ sub create_new_test { 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, + undef, $fingerprint, $seconds_between_tests_with_same_params, ); if ( $recent_hash_id ) { @@ -188,14 +188,14 @@ sub create_new_test { $batch_id, $priority, $queue_label, - $test_params_deterministic_hash, + $fingerprint, $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); + "SELECT id, hash_id FROM test_results WHERE params_deterministic_hash=? ORDER BY id DESC LIMIT 1", undef, $fingerprint); $result_id = $hash_id; } @@ -354,10 +354,11 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (domain, batch_id, priority, queue, params_deterministic_hash, params) 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 ) ); + my $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); - $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $test_params_deterministic_hash, $encoded_params ); + $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); + $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); } $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)" ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 187b664da..be04c17e6 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -170,8 +170,8 @@ sub create_new_test { $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 $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; @@ -188,14 +188,14 @@ sub create_new_test { $batch_id, $priority, $queue_label, - $test_params_deterministic_hash, + $fingerprint, $encoded_params, - $test_params_deterministic_hash, + $fingerprint, 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 ); + "SELECT id,hash_id FROM test_results WHERE params_deterministic_hash=? ORDER BY id DESC LIMIT 1", undef, $fingerprint ); return $hash_id; } @@ -326,10 +326,10 @@ sub add_batch_job { $dbh->do( "COPY test_results(batch_id, priority, queue, params_deterministic_hash, params) 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 ) ); + my $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); - $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$test_params_deterministic_hash\t$encoded_params\n"); + $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\n"); } $dbh->pg_putcopyend(); $dbh->do( "ALTER TABLE test_results ADD PRIMARY KEY (id)" ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 025b341ab..3c5d4cfbf 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -173,9 +173,9 @@ sub create_new_test { 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 $js = JSON::PP->new->canonical; + my $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( $encoded_params ); my $result_id; my $priority = $test_params->{priority}; @@ -186,7 +186,7 @@ sub create_new_test { 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, + $fingerprint, "-$seconds seconds" ); @@ -209,7 +209,7 @@ sub create_new_test { $batch_id, $priority, $queue_label, - $test_params_deterministic_hash, + $fingerprint, $encoded_params, $test_params->{domain}, ($test_params->{nameservers})?(1):(0), @@ -347,10 +347,10 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, params_deterministic_hash, params) 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 ) ); + my $encoded_params = $js->encode( $test_params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); - $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $test_params_deterministic_hash, $encoded_params ); + $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); } $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)" ); From a8ff24451ef49e4ba5229c78048038d7a09983a5 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 11:18:10 +0200 Subject: [PATCH 105/424] Generate fingerprint in a separate method --- lib/Zonemaster/Backend/DB.pm | 14 ++++++++++++++ lib/Zonemaster/Backend/DB/MySQL.pm | 4 +--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 ++---- lib/Zonemaster/Backend/DB/SQLite.pm | 6 +++--- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index b7b584c52..d0be92f92 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -7,6 +7,8 @@ use Moose::Role; use 5.14.2; use JSON::PP; +use Digest::MD5 qw(md5_hex); +use Encode; use Log::Any qw( $log ); requires qw( @@ -179,6 +181,18 @@ sub _new_dbh { return $dbh; } +sub generate_fingerprint { + my ( $self, $params ) = @_; + + my $js = JSON::PP->new; + $js->canonical( 1 ); + + my $encoded_params = $js->encode( $params ); + my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + + return ( $fingerprint, $encoded_params ); +} + no Moose::Role; 1; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 872ce677c..ff2faae05 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -158,9 +158,7 @@ sub create_new_test { my $dbh = $self->dbh; $test_params->{domain} = $domain; - my $js = JSON::PP->new->canonical; - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( $encoded_params ); + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); my $result_id; my $priority = $test_params->{priority}; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index be04c17e6..5bac56a5e 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -168,10 +168,8 @@ sub create_new_test { my $dbh = $self->dbh; $test_params->{domain} = $domain; - my $js = JSON::PP->new; - $js->canonical( 1 ); - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 3c5d4cfbf..5e92e228d 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -173,9 +173,9 @@ sub create_new_test { my $dbh = $self->dbh; $test_params->{domain} = $domain; - my $js = JSON::PP->new->canonical; - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( $encoded_params ); + + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + my $result_id; my $priority = $test_params->{priority}; From 5d8babbb75cea9d0b39b62445401c13864710175 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 11:27:14 +0200 Subject: [PATCH 106/424] Use method to generate fingerprint in batch job --- lib/Zonemaster/Backend/DB/MySQL.pm | 5 +---- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 5 +---- lib/Zonemaster/Backend/DB/SQLite.pm | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ff2faae05..8b02d4c43 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -332,8 +332,6 @@ 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} ); @@ -352,8 +350,7 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 5bac56a5e..e8c707b45 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -302,8 +302,6 @@ 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} ); @@ -324,8 +322,7 @@ sub add_batch_job { $dbh->do( "COPY test_results(batch_id, priority, queue, params_deterministic_hash, params) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\n"); } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 5e92e228d..a864c5f37 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -327,8 +327,6 @@ 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} ); @@ -347,8 +345,7 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $js->encode( $test_params ); - my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); + my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); } From 80c0fcacd8df4dc3afbb6ce7c9dee11597ab4d68 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 11:33:21 +0200 Subject: [PATCH 107/424] Remove unused modules --- lib/Zonemaster/Backend/DB/MySQL.pm | 2 -- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 1 - lib/Zonemaster/Backend/DB/SQLite.pm | 1 - 3 files changed, 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 8b02d4c43..f08759319 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -7,8 +7,6 @@ use 5.14.2; use Data::Dumper; use DBI qw(:utils); -use Digest::MD5 qw(md5_hex); -use Encode; use JSON::PP; use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e8c707b45..e082934db 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -6,7 +6,6 @@ use Moose; use 5.14.2; use DBI qw(:utils); -use Digest::MD5 qw(md5_hex); use Encode; use JSON::PP; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index a864c5f37..9c3f9db0f 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -8,7 +8,6 @@ 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 ); From 1472df59be0a9c484dd68f24fc2b0fe229bb53d6 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 15:34:23 +0200 Subject: [PATCH 108/424] Specify properties to keep in fingerprint If a property is missing, set it to a default value --- lib/Zonemaster/Backend/DB.pm | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index d0be92f92..3794f434a 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -11,6 +11,8 @@ use Digest::MD5 qw(md5_hex); use Encode; use Log::Any qw( $log ); +use Zonemaster::Engine::Profile + requires qw( add_api_user_to_db add_batch_job @@ -184,10 +186,22 @@ sub _new_dbh { sub generate_fingerprint { my ( $self, $params ) = @_; + my $profile = Zonemaster::Engine::Profile->effective; + + my %to_encode = (); + $to_encode{domain} = $$params{domain} // ""; + $to_encode{ds_info} = $$params{ds_info} // []; + $to_encode{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); + $to_encode{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); + $to_encode{nameservers} = $$params{nameservers} // []; + $to_encode{priority} = $$params{priority} // 10; + $to_encode{profile} = $$params{profile} // "default"; + $to_encode{queue} = $$params{queue} // 0; + my $js = JSON::PP->new; $js->canonical( 1 ); - my $encoded_params = $js->encode( $params ); + my $encoded_params = $js->encode( \%to_encode ); my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); return ( $fingerprint, $encoded_params ); From edaf61b4e2a0312bddc5bcf31857a4ab410fe678 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 17:46:24 +0200 Subject: [PATCH 109/424] Make domain case insensitive --- lib/Zonemaster/Backend/DB.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 3794f434a..89db4fdf6 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -189,7 +189,7 @@ sub generate_fingerprint { my $profile = Zonemaster::Engine::Profile->effective; my %to_encode = (); - $to_encode{domain} = $$params{domain} // ""; + $to_encode{domain} = lc $$params{domain} // ""; $to_encode{ds_info} = $$params{ds_info} // []; $to_encode{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); $to_encode{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); From c16a3058d742ba41c61efd2a2169326502832225 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 17 Jun 2021 18:01:50 +0200 Subject: [PATCH 110/424] Unit tests for database fingerprint --- t/db.t | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 t/db.t diff --git a/t/db.t b/t/db.t new file mode 100644 index 000000000..7bc70bd2d --- /dev/null +++ b/t/db.t @@ -0,0 +1,51 @@ +use strict; +use warnings; + +use Test::More; # see done_testing() + +use_ok( 'Zonemaster::Backend::DB' ); +use_ok( 'JSON::PP' ); + +sub generate_fingerprint { + return Zonemaster::Backend::DB::generate_fingerprint( undef, shift ); +} + +subtest 'encoding and fingerprint' => sub { + + subtest 'missing properties' => sub { + my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; + + my %params = ( domain => "example.com" ); + + my ( $fingerprint, $encoded_params ) = generate_fingerprint( \%params ); + is $encoded_params, $expected_encoded_params, 'domain only: the encoded strings should match'; + #diag ($fingerprint); + + $params{ipv4} = JSON::PP->true; + my ( $fingerprint_ipv4, $encoded_params_ipv4 ) = generate_fingerprint( \%params ); + is $encoded_params_ipv4, $expected_encoded_params, 'add ipv4: the encoded strings should match'; + is $fingerprint_ipv4, $fingerprint, 'fingerprints should match'; + }; + + subtest 'should be case insensitive' => sub { + my %params1 = ( domain => "example.com" ); + my %params2 = ( domain => "eXamPLe.COm" ); + + my ( $fingerprint1, $encoded_params1 ) = generate_fingerprint( \%params1 ); + my ( $fingerprint2, $encoded_params2 ) = generate_fingerprint( \%params2 ); + is $fingerprint1, $fingerprint2, 'same fingerprint'; + is $encoded_params1, $encoded_params2, 'same encoded string'; + }; + + subtest 'garbage properties set' => sub { + my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; + my %params = ( + domain => "example.com", + client => "GUI v3.3.0" + ); + my ( $fingerprint, $encoded_params ) = generate_fingerprint( \%params ); + is $encoded_params, $expected_encoded_params, 'leave out garbage property'; + }; +}; + +done_testing(); From 03cfc636a2dbdbaaf5138758fa7355613d7b4835 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 21 Jun 2021 16:01:03 +0200 Subject: [PATCH 111/424] Sort array of hashes The ``ip'' key from a ``nameserver'' object is not mandatory as specified in the API.md document. --- lib/Zonemaster/Backend/DB.pm | 25 +++++++++++++++++-- t/db.t | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 89db4fdf6..cb475bd4b 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -190,14 +190,35 @@ sub generate_fingerprint { my %to_encode = (); $to_encode{domain} = lc $$params{domain} // ""; - $to_encode{ds_info} = $$params{ds_info} // []; $to_encode{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); $to_encode{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); - $to_encode{nameservers} = $$params{nameservers} // []; $to_encode{priority} = $$params{priority} // 10; $to_encode{profile} = $$params{profile} // "default"; $to_encode{queue} = $$params{queue} // 0; + 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; + + $to_encode{ds_info} = \@array_ds_info_sort; + + my $array_nameservers = $$params{nameservers} // []; + my @array_nameservers_sort = sort { + ( + defined $a->{ip} and + defined $b->{ip} and + $a->{ip} cmp $b->{ip} + ) or + $a->{ns} cmp $b->{ns} + } @$array_nameservers; + + $to_encode{nameservers} = \@array_nameservers_sort; + + my $js = JSON::PP->new; $js->canonical( 1 ); diff --git a/t/db.t b/t/db.t index 7bc70bd2d..d56b2ed59 100644 --- a/t/db.t +++ b/t/db.t @@ -27,6 +27,54 @@ subtest 'encoding and fingerprint' => sub { 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 ) = generate_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = generate_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" } + ] + ); + my %params2 = ( + nameservers => [ + { ns => "ns1.nic.fr" }, + { ip => "192.134.4.1", ns => "ns2.nic.fr"} + ], + domain => "example.com" + ); + my ( $encoded_params1, $fingerprint1 ) = generate_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = generate_fingerprint( \%params2 ); + is $fingerprint1, $fingerprint2, 'nameservers: same fingerprint'; + is $encoded_params1, $encoded_params2, 'nameservers: same encoded string'; + }; + }; + subtest 'should be case insensitive' => sub { my %params1 = ( domain => "example.com" ); my %params2 = ( domain => "eXamPLe.COm" ); From 9ce8b5b2fa5c4eb0ec499fb1e04cd67f8cbdeda3 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 21 Jun 2021 17:43:18 +0200 Subject: [PATCH 112/424] Split normalization, encoding and fingerprinting --- lib/Zonemaster/Backend/DB.pm | 37 +++++++++++++++++-------- lib/Zonemaster/Backend/DB/MySQL.pm | 9 ++++-- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 7 +++-- lib/Zonemaster/Backend/DB/SQLite.pm | 7 +++-- t/db.t | 28 +++++++++++-------- 5 files changed, 59 insertions(+), 29 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index cb475bd4b..76de7b966 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -183,18 +183,18 @@ sub _new_dbh { return $dbh; } -sub generate_fingerprint { +sub _normalize_parameter_hash { my ( $self, $params ) = @_; my $profile = Zonemaster::Engine::Profile->effective; - my %to_encode = (); - $to_encode{domain} = lc $$params{domain} // ""; - $to_encode{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); - $to_encode{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); - $to_encode{priority} = $$params{priority} // 10; - $to_encode{profile} = $$params{profile} // "default"; - $to_encode{queue} = $$params{queue} // 0; + my %normalized = (); + $normalized{domain} = lc $$params{domain} // ""; + $normalized{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); + $normalized{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); + $normalized{profile} = $$params{profile} // "default"; + $normalized{priority} = $$params{priority} // 10; + $normalized{queue} = $$params{queue} // 0; my $array_ds_info = $$params{ds_info} // []; my @array_ds_info_sort = sort { @@ -204,7 +204,7 @@ sub generate_fingerprint { $a->{keytag} <=> $b->{keytag} } @$array_ds_info; - $to_encode{ds_info} = \@array_ds_info_sort; + $normalized{ds_info} = \@array_ds_info_sort; my $array_nameservers = $$params{nameservers} // []; my @array_nameservers_sort = sort { @@ -216,16 +216,29 @@ sub generate_fingerprint { $a->{ns} cmp $b->{ns} } @$array_nameservers; - $to_encode{nameservers} = \@array_nameservers_sort; + $normalized{nameservers} = \@array_nameservers_sort; + + return \%normalized; +} + +sub encode_normalized_params { + my ( $self, $params ) = @_; + my $normalized_params = $self->_normalize_parameter_hash( $params ); my $js = JSON::PP->new; $js->canonical( 1 ); - my $encoded_params = $js->encode( \%to_encode ); + my $encoded_params = $js->encode( $normalized_params ); + + return $encoded_params; +} + +sub generate_fingerprint { + my ( $self, $encoded_params ) = @_; my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); - return ( $fingerprint, $encoded_params ); + return $fingerprint; } no Moose::Role; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index f08759319..e79fe1783 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -156,7 +156,10 @@ sub create_new_test { my $dbh = $self->dbh; $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $result_id; my $priority = $test_params->{priority}; @@ -348,7 +351,9 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e082934db..178e05155 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -168,7 +168,8 @@ sub create_new_test { $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; @@ -321,7 +322,9 @@ sub add_batch_job { $dbh->do( "COPY test_results(batch_id, priority, queue, params_deterministic_hash, params) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\n"); } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 9c3f9db0f..de0cfe89b 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -173,7 +173,8 @@ sub create_new_test { $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); my $result_id; @@ -344,7 +345,9 @@ sub add_batch_job { my $sth = $dbh->prepare( 'INSERT INTO test_results (hash_id, domain, batch_id, priority, queue, params_deterministic_hash, params) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my ( $fingerprint, $encoded_params ) = $self->generate_fingerprint( $test_params ); + + my $encoded_params = $self->encode_normalized_params( $test_params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); } diff --git a/t/db.t b/t/db.t index d56b2ed59..48fb20a1e 100644 --- a/t/db.t +++ b/t/db.t @@ -6,8 +6,14 @@ use Test::More; # see done_testing() use_ok( 'Zonemaster::Backend::DB' ); use_ok( 'JSON::PP' ); -sub generate_fingerprint { - return Zonemaster::Backend::DB::generate_fingerprint( undef, shift ); +sub encode_and_fingerprint { + my $params = shift; + + my $self = "Zonemaster::Backend::DB"; + my $encoded_params = $self->encode_normalized_params( $params ); + my $fingerprint = $self->generate_fingerprint( $encoded_params ); + + return ( $encoded_params, $fingerprint ); } subtest 'encoding and fingerprint' => sub { @@ -17,12 +23,12 @@ subtest 'encoding and fingerprint' => sub { my %params = ( domain => "example.com" ); - my ( $fingerprint, $encoded_params ) = generate_fingerprint( \%params ); + 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 ( $fingerprint_ipv4, $encoded_params_ipv4 ) = generate_fingerprint( \%params ); + 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'; }; @@ -47,8 +53,8 @@ subtest 'encoding and fingerprint' => sub { }], domain => "example.com" ); - my ( $encoded_params1, $fingerprint1 ) = generate_fingerprint( \%params1 ); - my ( $encoded_params2, $fingerprint2 ) = generate_fingerprint( \%params2 ); + 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'; }; @@ -68,8 +74,8 @@ subtest 'encoding and fingerprint' => sub { ], domain => "example.com" ); - my ( $encoded_params1, $fingerprint1 ) = generate_fingerprint( \%params1 ); - my ( $encoded_params2, $fingerprint2 ) = generate_fingerprint( \%params2 ); + my ( $encoded_params1, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( $encoded_params2, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); is $fingerprint1, $fingerprint2, 'nameservers: same fingerprint'; is $encoded_params1, $encoded_params2, 'nameservers: same encoded string'; }; @@ -79,8 +85,8 @@ subtest 'encoding and fingerprint' => sub { my %params1 = ( domain => "example.com" ); my %params2 = ( domain => "eXamPLe.COm" ); - my ( $fingerprint1, $encoded_params1 ) = generate_fingerprint( \%params1 ); - my ( $fingerprint2, $encoded_params2 ) = generate_fingerprint( \%params2 ); + 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'; }; @@ -91,7 +97,7 @@ subtest 'encoding and fingerprint' => sub { domain => "example.com", client => "GUI v3.3.0" ); - my ( $fingerprint, $encoded_params ) = generate_fingerprint( \%params ); + my ( $encoded_params, $fingerprint ) = encode_and_fingerprint( \%params ); is $encoded_params, $expected_encoded_params, 'leave out garbage property'; }; }; From 6e22c16d8730ae83b0de5b8e9ce65de7bdf31ffb Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 23 Jun 2021 18:07:19 +0200 Subject: [PATCH 113/424] Normalize ip key in nameserver object If the object does not contain the ``ip'' key, then add the key with an empty string as value. --- lib/Zonemaster/Backend/DB.pm | 9 ++++----- t/db.t | 13 +++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 76de7b966..11171bd71 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -207,12 +207,11 @@ sub _normalize_parameter_hash { $normalized{ds_info} = \@array_ds_info_sort; my $array_nameservers = $$params{nameservers} // []; + for my $nameserver (@$array_nameservers) { + $$nameserver{ip} = "" if ( not defined $$nameserver{ip} ); + } my @array_nameservers_sort = sort { - ( - defined $a->{ip} and - defined $b->{ip} and - $a->{ip} cmp $b->{ip} - ) or + $a->{ip} cmp $b->{ip} or $a->{ns} cmp $b->{ns} } @$array_nameservers; diff --git a/t/db.t b/t/db.t index 48fb20a1e..e81b36421 100644 --- a/t/db.t +++ b/t/db.t @@ -74,10 +74,23 @@ subtest 'encoding and fingerprint' => sub { ], domain => "example.com" ); + my %params3 = ( + domain => "example.com", + nameservers => [ + { ip => "", ns => "ns1.nic.fr" }, + { ns => "ns2.nic.fr", ip => "192.134.4.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 ); + 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)'; }; }; From 6ccdbf1f1042266f635858032c5fcfb454f89b2c Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 28 Jun 2021 17:13:01 +0200 Subject: [PATCH 114/424] Ignore case for nameservers/*/ns value --- lib/Zonemaster/Backend/DB.pm | 1 + t/db.t | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 11171bd71..5651402a7 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -209,6 +209,7 @@ sub _normalize_parameter_hash { my $array_nameservers = $$params{nameservers} // []; for my $nameserver (@$array_nameservers) { $$nameserver{ip} = "" if ( not defined $$nameserver{ip} ); + $$nameserver{ns} = lc $$nameserver{ns}; } my @array_nameservers_sort = sort { $a->{ip} cmp $b->{ip} or diff --git a/t/db.t b/t/db.t index e81b36421..5eb00757a 100644 --- a/t/db.t +++ b/t/db.t @@ -81,16 +81,27 @@ subtest 'encoding and fingerprint' => sub { { 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 => "" } + ] + ); 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)'; }; }; From c95f15c311ff4c4a81ed90e029588ef87cb5711d Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 28 Jun 2021 18:16:19 +0200 Subject: [PATCH 115/424] Add POD --- lib/Zonemaster/Backend/DB.pm | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 5651402a7..62a078987 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -221,6 +221,14 @@ sub _normalize_parameter_hash { return \%normalized; } +=head2 encode_normalized_params + +Returns a normalized JSON string based on the provided parameters. +Each entry is set to a default value, see +L + +=cut + sub encode_normalized_params { my ( $self, $params ) = @_; @@ -234,6 +242,13 @@ sub encode_normalized_params { return $encoded_params; } +=head2 generate_fingerprint + +Returns a fingerprint of the normalized parameters passed in argument. +Such fingerprint are usefull to find similar tests in the database. + +=cut + sub generate_fingerprint { my ( $self, $encoded_params ) = @_; my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); From 28d258831065356196632af93b522db2ef5b18ce Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 28 Jun 2021 18:42:39 +0200 Subject: [PATCH 116/424] Update API documentation --- docs/API.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/API.md b/docs/API.md index 60541c1f6..d610eab3e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -575,8 +575,8 @@ An object with the property: 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"` @@ -639,11 +639,11 @@ 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. +* `"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*, optional. (default: `"default"`). Run the tests using the given profile. * `"config"`: **No longer supported**. Use `"profile"` instead. * `"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 @@ -1045,13 +1045,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. +* `"profile"`: A *profile name*, optional (default: `"default"`). Run the tests using the given profile. * `"config"`: **No longer supported**. Use `"profile"` instead. * `"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`) @@ -1196,8 +1196,12 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t > [Available profiles]: Configuration.md#profiles-section +[DS info]: #ds-info [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 [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag +[Name server]: #name-server +[net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 +[net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 [Privilege levels]: #privilege-levels From 9a0f5f36701ab107183bac61ffc1e78cc32ae694 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 1 Jul 2021 11:21:48 +0200 Subject: [PATCH 117/424] Remove ``ip'' key if empty This key is not mandatory, if its value is the empty string, then the key is removed from the ``nameservers'' object. The ``nameservers'' object is sorted by order of its ``ns'' values and ``ip'' values (if defined). --- lib/Zonemaster/Backend/DB.pm | 8 +++++--- t/db.t | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 62a078987..d332baeb1 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -208,12 +208,14 @@ sub _normalize_parameter_hash { my $array_nameservers = $$params{nameservers} // []; for my $nameserver (@$array_nameservers) { - $$nameserver{ip} = "" if ( not defined $$nameserver{ip} ); + if ( defined $$nameserver{ip} and $$nameserver{ip} eq "" ) { + delete $$nameserver{ip}; + } $$nameserver{ns} = lc $$nameserver{ns}; } my @array_nameservers_sort = sort { - $a->{ip} cmp $b->{ip} or - $a->{ns} cmp $b->{ns} + $a->{ns} cmp $b->{ns} or + ( defined $a->{ip} and defined $b->{ip} and $a->{ip} cmp $b->{ip} ) } @$array_nameservers; $normalized{nameservers} = \@array_nameservers_sort; diff --git a/t/db.t b/t/db.t index 5eb00757a..c2729dfca 100644 --- a/t/db.t +++ b/t/db.t @@ -64,11 +64,13 @@ subtest 'encoding and fingerprint' => sub { domain => "example.com", nameservers => [ { ns => "ns2.nic.fr", ip => "192.134.4.1" }, - { ns => "ns1.nic.fr" } + { 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"} ], @@ -78,6 +80,7 @@ subtest 'encoding and fingerprint' => sub { 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" } ] ); @@ -85,7 +88,8 @@ subtest 'encoding and fingerprint' => sub { domain => "example.com", nameservers => [ { ip => "192.134.4.1", ns => "nS2.Nic.FR"}, - { ns => "Ns1.nIC.fR", ip => "" } + { ns => "Ns1.nIC.fR", ip => "" }, + { ns => "ns3.nic.fr", ip => "192.0.2.1" } ] ); From 4d686bc6fddb724ad2cf4b71bdf87ba78b1efba1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 11:00:46 +0200 Subject: [PATCH 118/424] Remove documentation on unsupported property --- docs/API.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/API.md b/docs/API.md index d610eab3e..fe00451f9 100644 --- a/docs/API.md +++ b/docs/API.md @@ -644,7 +644,6 @@ An object with the following properties: * `"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*, optional. (default: `"default"`). Run the tests using the given profile. -* `"config"`: **No longer supported**. Use `"profile"` instead. * `"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`) @@ -754,7 +753,6 @@ Example response: "domain": "zonemaster.net", "profile": "default", "ipv6": true, - "advanced": true, "nameservers": [ { "ns": "ns3.nic.se", @@ -865,13 +863,11 @@ Example response: "id": "c45a3f8256c4a155", "creation_time": "2016-11-15 11:53:13.965982", "overall_result": "error", - "advanced_options": null }, { "id": "32dd4bc0582b6bf9", "creation_time": "2016-11-14 08:46:41.532047", "overall_result": "error", - "advanced_options": null }, ... ] @@ -1046,7 +1042,6 @@ 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"`: **No longer supported**. Use `"profile"` instead. * `"client_version"`: A *client version*, optional. (default: unset) * `"nameservers"`: A list of [*name server*][Name server] objects, optional. (default: `[]`) * `"ds_info"`: A list of [*DS info*][DS info] objects, optional. (default: `[]`) @@ -1157,7 +1152,6 @@ Example response: "domain": "zonemaster.net", "profile": "default", "client_id": "Zonemaster Dancer Frontend", - "advanced": true, "nameservers": [ { "ns": "ns3.nic.se", From 887b783ebddde402a67e65627bfe49886e95fe0f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 11:28:00 +0200 Subject: [PATCH 119/424] Update MANIFEST --- MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST b/MANIFEST index 414f85a5e..1d793cf14 100644 --- a/MANIFEST +++ b/MANIFEST @@ -66,6 +66,7 @@ share/zm-testagent.lsb share/zm_rpcapi-bsd share/zm_testagent-bsd t/config.t +t/db.t t/test01.data t/test01.t t/test_validate_syntax.t From 4a49fc8aa5d8d2b09103d6a7bbd04d90c520c52e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 12:33:31 +0200 Subject: [PATCH 120/424] Keep other params properties in database Compute the fingerprint using a subset of the ``params'' properties but store all of the properties in the database. Reuse the normalization of some of the properties before storage though. --- lib/Zonemaster/Backend/DB.pm | 39 ++++++++++++++++--------- lib/Zonemaster/Backend/DB/MySQL.pm | 8 ++--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 8 ++--- lib/Zonemaster/Backend/DB/SQLite.pm | 8 ++--- t/db.t | 18 ++++++++---- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index d332baeb1..c5fe8e559 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -183,7 +183,7 @@ sub _new_dbh { return $dbh; } -sub _normalize_parameter_hash { +sub _normalize_params { my ( $self, $params ) = @_; my $profile = Zonemaster::Engine::Profile->effective; @@ -223,36 +223,49 @@ sub _normalize_parameter_hash { return \%normalized; } -=head2 encode_normalized_params +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; +} -Returns a normalized JSON string based on the provided parameters. -Each entry is set to a default value, see +=head2 encode_params + +Encode the params object into a JSON string. The object is first normalized and +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_normalized_params { +sub encode_params { my ( $self, $params ) = @_; - my $normalized_params = $self->_normalize_parameter_hash( $params ); - - my $js = JSON::PP->new; - $js->canonical( 1 ); - - my $encoded_params = $js->encode( $normalized_params ); + my $normalized_params = $self->_normalize_params( $params ); + $params = { %$params, %$normalized_params }; + my $encoded_params = $self->_params_to_json_str( $params ); return $encoded_params; } =head2 generate_fingerprint -Returns a fingerprint of the normalized parameters passed in argument. +Returns a fingerprint of the hash passed in argument. +The fingerprint is computed after normalizing the hash. Such fingerprint are usefull to find similar tests in the database. =cut sub generate_fingerprint { - my ( $self, $encoded_params ) = @_; + my ( $self, $params ) = @_; + + my $normalized_params = $self->_normalize_params( $params ); + my $encoded_params = $self->_params_to_json_str( $normalized_params ); my $fingerprint = md5_hex( encode_utf8( $encoded_params ) ); return $fingerprint; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index e79fe1783..e9cb2b2b0 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -157,8 +157,8 @@ sub create_new_test { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); my $result_id; @@ -352,8 +352,8 @@ sub add_batch_job { foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 178e05155..67bf6a1f5 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -168,8 +168,8 @@ sub create_new_test { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; @@ -323,8 +323,8 @@ sub add_batch_job { foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\n"); } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index de0cfe89b..94ae2cbcd 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -173,8 +173,8 @@ sub create_new_test { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); my $result_id; @@ -346,8 +346,8 @@ sub add_batch_job { foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; - my $encoded_params = $self->encode_normalized_params( $test_params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $fingerprint = $self->generate_fingerprint( $test_params ); + my $encoded_params = $self->encode_params( $test_params ); $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); } diff --git a/t/db.t b/t/db.t index c2729dfca..30da86ffa 100644 --- a/t/db.t +++ b/t/db.t @@ -10,8 +10,8 @@ sub encode_and_fingerprint { my $params = shift; my $self = "Zonemaster::Backend::DB"; - my $encoded_params = $self->encode_normalized_params( $params ); - my $fingerprint = $self->generate_fingerprint( $encoded_params ); + my $encoded_params = $self->encode_params( $params ); + my $fingerprint = $self->generate_fingerprint( $params ); return ( $encoded_params, $fingerprint ); } @@ -120,13 +120,19 @@ subtest 'encoding and fingerprint' => sub { }; subtest 'garbage properties set' => sub { - my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; - my %params = ( + my $expected_encoded_params = '{"client":"GUI v3.3.0","domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; + my %params1 = ( + domain => "example.com", + ); + my %params2 = ( domain => "example.com", client => "GUI v3.3.0" ); - my ( $encoded_params, $fingerprint ) = encode_and_fingerprint( \%params ); - is $encoded_params, $expected_encoded_params, 'leave out garbage property'; + 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'; }; }; From 1355bcc5db324a15e31331583c12de64c90778da Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 11:26:05 +0200 Subject: [PATCH 121/424] Update API on normalized params --- docs/API.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/API.md b/docs/API.md index fe00451f9..72b1d8536 100644 --- a/docs/API.md +++ b/docs/API.md @@ -807,8 +807,8 @@ 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. +* `"params"`: A normalized version `"params"` object sent to + `start_domain_test` when the *test* was started. * `"results"`: A list of *test result* objects. @@ -816,9 +816,10 @@ 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. +* `"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"` @@ -1129,7 +1130,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: From a53df6a40e35623eb0273a645930c5560092b26a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 16:34:56 +0200 Subject: [PATCH 122/424] Add database unit test --- t/test01.t | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/t/test01.t b/t/test01.t index a9f21b7de..3c73994dc 100644 --- a/t/test01.t +++ b/t/test01.t @@ -157,6 +157,40 @@ 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)"; + +}; + if ( $ENV{ZONEMASTER_RECORD} ) { Zonemaster::Engine->save_cache( $datafile ); } From a2d05316d4656b1a086a0ad2d90edf44f41418d3 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 5 Jul 2021 17:55:40 +0200 Subject: [PATCH 123/424] Comment on duplicated default values with RPCAPI To test the computation of the fingerprint without using the RPCAPI, the current logic needs to set a default value for each kept property in the ``_normalize_params'' method. --- lib/Zonemaster/Backend/DB.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index c5fe8e559..d108b77da 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -189,6 +189,9 @@ sub _normalize_params { my $profile = Zonemaster::Engine::Profile->effective; my %normalized = (); + + # some of these values are already set in RPCAPI + # however setting them here again is required for testing purpose $normalized{domain} = lc $$params{domain} // ""; $normalized{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); $normalized{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); From 91b404ed2a3e41fd97e6998e1bf07274625df93e Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 6 Jul 2021 14:29:04 +0200 Subject: [PATCH 124/424] Corrects typo --- script/zmb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/zmb b/script/zmb index 59723d82e..404fa0a6e 100755 --- a/script/zmb +++ b/script/zmb @@ -368,7 +368,7 @@ sub cmd_get_test_history { ); if ( $opt_filter ) { - unless ( $opt_filer =~ /^(?:all|delegated|undelegated)$/ ) { + unless ( $opt_filter =~ /^(?:all|delegated|undelegated)$/ ) { die 'Illegal filter value. Expects "all", "delegated" or "undelegated" '; } $params{filter} = $opt_filter; From 49d9e76622e29e1fa01fd6f57524ddc6cb9f1d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 9 Apr 2021 22:37:07 +0200 Subject: [PATCH 125/424] Add new accessors for profile sections --- lib/Zonemaster/Backend/Config.pm | 53 ++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 1b301368a..fb728d1d9 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -100,6 +100,8 @@ 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"; @@ -132,6 +134,7 @@ sub parse { $obj->_set_ZONEMASTER_lock_on_queue( '0' ); $obj->_set_ZONEMASTER_age_reuse_previous_test( '600' ); $obj->_add_LANGUAGE_locale( 'en_US' ); + $obj->_add_public_profile( 'default', undef ); # Assign property values (part 1/2) if ( defined( my $value = $get_and_clear->( 'DB', 'engine' ) ) ) { @@ -269,15 +272,14 @@ sub parse { } } - $obj->{_public_profiles} = { - default => undef, - }; 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) @@ -486,6 +488,25 @@ E.g.: ) +=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. @@ -548,6 +569,8 @@ sub POSTGRESQL_password { return $_[0]->{_POSTGR 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}; } @@ -741,6 +764,24 @@ sub _reset_LANGUAGE_locale { return; } +sub _add_public_profile { + my ( $self, $name, $path ) = @_; + + $name = lc $name; + + $self->{_public_profiles}{$name} = $path; + return; +} + +sub _add_private_profile { + my ( $self, $name, $path ) = @_; + + $name = lc $name; + + $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, $validate ) = @_; From f846509cd62ad8feb92e9a3c8657b06873401eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 15 Apr 2021 21:11:38 +0200 Subject: [PATCH 126/424] Validate profile names --- lib/Zonemaster/Backend/Config.pm | 26 ++++++ lib/Zonemaster/Backend/TestAgent.pm | 1 - lib/Zonemaster/Backend/Validator.pm | 7 ++ t/config.t | 132 ++++++++++++++++++++++++++++ t/validator.t | 17 ++++ 5 files changed, 182 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index fb728d1d9..6a49fbb20 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -767,8 +767,20 @@ sub _reset_LANGUAGE_locale { 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; } @@ -776,8 +788,22 @@ sub _add_public_profile { 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; } diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index 48c048f6a..dc05672cd 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -42,7 +42,6 @@ sub new { foreach my $name ( keys %all_profiles ) { my $path = $all_profiles{$name}{profile_file_name}; - die "default profile cannot be private" if ( $name eq 'default' && $all_profiles{$name}{type} eq 'private' ); my $full_profile = Zonemaster::Engine::Profile->default; if ( $path ne "" ) { my $json = eval { read_file( $path, err_mode => 'croak' ) } # diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 2d743eae6..2ab5b79c6 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -26,6 +26,7 @@ our @EXPORT_OK = qw( untaint_non_negative_int untaint_password untaint_postgresql_ident + untaint_profile_name untaint_strictly_positive_int untaint_strictly_positive_millis ); @@ -46,6 +47,7 @@ our %EXPORT_TAGS = ( untaint_non_negative_int untaint_password untaint_postgresql_ident + untaint_profile_name untaint_strictly_positive_int untaint_strictly_positive_millis ) @@ -310,6 +312,11 @@ sub untaint_non_negative_int { return _untaint_pat( $value, $NON_NEGATIVE_INT_RE ); } +sub untaint_profile_name { + my ( $value ) = @_; + return _untaint_pat( $value, $PROFILE_NAME_RE ); +} + sub _untaint_pat { my ( $value, @patterns ) = @_; diff --git a/t/config.t b/t/config.t index 826c85f02..6cdea97a9 100644 --- a/t/config.t +++ b/t/config.t @@ -43,6 +43,14 @@ subtest 'Everything but NoWarnings' => sub { [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 @@ -67,6 +75,16 @@ subtest 'Everything but NoWarnings' => sub { 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_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'; @@ -88,6 +106,8 @@ subtest 'Everything but NoWarnings' => sub { 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'; @@ -710,6 +730,118 @@ 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] + 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/validator.t b/t/validator.t index 068f21b6d..07423fdba 100644 --- a/t/validator.t +++ b/t/validator.t @@ -152,6 +152,23 @@ subtest 'Everything but NoWarnings' => sub { 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'; From a28f0a8d16fd76b32fcb87943daa24c6b6a4a1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 18:06:43 +0200 Subject: [PATCH 127/424] Refactor: Use {PUBLIC,PRIVATE}_PROFILES --- lib/Zonemaster/Backend/Config.pm | 23 ----------------------- lib/Zonemaster/Backend/RPCAPI.pm | 13 +++++++------ lib/Zonemaster/Backend/TestAgent.pm | 6 +++--- 3 files changed, 10 insertions(+), 32 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 6a49fbb20..9b7532b80 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -600,29 +600,6 @@ UNITCHECK { _create_setter( '_set_ZONEMASTER_age_reuse_previous_test', '_ZONEMASTER_age_reuse_previous_test', \&untaint_strictly_positive_int ); } -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} }; -} - =head2 new_DB Create a new database adapter object according to configuration. diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 6d18d1b38..858fce647 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -100,13 +100,13 @@ $json_schemas{profile_names} = joi->object->strict; sub profile_names { my ( $self ) = @_; - my @profiles; - eval { @profiles = $self->{config}->ListPublicProfiles() }; + my %profiles; + eval { %profiles = $self->{config}->PUBLIC_PROFILES }; if ( $@ ) { handle_exception( 'profile_names', $@, '004' ); } - return \@profiles; + return [ keys %profiles ]; } # Return the list of language tags supported by get_test_results(). The tags are @@ -295,9 +295,10 @@ sub validate_syntax { } 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 %profiles = $self->{config}->PUBLIC_PROFILES; + if ( !exists $profiles{ $syntax_input->{profile} } ) { + return { status => 'nok', message => encode_entities( "Unrecognized profile name" ) }; + } } my ( undef, $dn_syntax ) = $self->_check_domain( $syntax_input->{domain}, 'Domain name' ); diff --git a/lib/Zonemaster/Backend/TestAgent.pm b/lib/Zonemaster/Backend/TestAgent.pm index dc05672cd..b08e100f6 100644 --- a/lib/Zonemaster/Backend/TestAgent.pm +++ b/lib/Zonemaster/Backend/TestAgent.pm @@ -38,12 +38,12 @@ sub new { my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); $self->{_db} = $dbclass->from_config( $config ); - my %all_profiles = %{ $config->ReadProfilesInfo() }; + my %all_profiles = ( $config->PUBLIC_PROFILES, $config->PRIVATE_PROFILES ); foreach my $name ( keys %all_profiles ) { - my $path = $all_profiles{$name}{profile_file_name}; + my $path = $all_profiles{$name}; my $full_profile = Zonemaster::Engine::Profile->default; - if ( $path ne "" ) { + 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 ) } # From 0ab7a13a586a81023b088f97bb23122395ea401a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Mon, 21 Jun 2021 20:51:51 +0200 Subject: [PATCH 128/424] Fix: Allow using private profiles --- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 858fce647..d7a749bb7 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -295,7 +295,7 @@ sub validate_syntax { } if ( defined $syntax_input->{profile} ) { - my %profiles = $self->{config}->PUBLIC_PROFILES; + my %profiles = ( $self->{config}->PUBLIC_PROFILES, $self->{config}->PRIVATE_PROFILES ); if ( !exists $profiles{ $syntax_input->{profile} } ) { return { status => 'nok', message => encode_entities( "Unrecognized profile name" ) }; } From ca97d356bc2215ada2cedd226fbd8cba0872365c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Wed, 7 Jul 2021 16:09:26 +0200 Subject: [PATCH 129/424] Forbid repeated keys in the same config section --- docs/Configuration.md | 1 + lib/Zonemaster/Backend/Config.pm | 5 ++++- t/config.t | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 7ff1955c4..199e7147b 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -10,6 +10,7 @@ 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. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 9b7532b80..ad4d26ebb 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -108,7 +108,10 @@ sub parse { 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; }; diff --git a/t/config.t b/t/config.t index 6cdea97a9..17a18d4de 100644 --- a/t/config.t +++ b/t/config.t @@ -809,6 +809,38 @@ subtest 'Everything but NoWarnings' => sub { } 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] From f22628ad03e2fdc849d30bafbae973495b18a05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 8 Jul 2021 09:57:42 +0200 Subject: [PATCH 130/424] Normalize profile argument --- lib/Zonemaster/Backend/RPCAPI.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index d7a749bb7..3ff83edca 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -295,6 +295,7 @@ sub validate_syntax { } 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} } ) { return { status => 'nok', message => encode_entities( "Unrecognized profile name" ) }; From 98e5bcf6047b9a782216fd0f27f217ff02553dc4 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 8 Jul 2021 15:47:37 +0200 Subject: [PATCH 131/424] Corrects the description of the reuse time --- docs/API.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 72b1d8536..a57f08acf 100644 --- a/docs/API.md +++ b/docs/API.md @@ -658,8 +658,15 @@ An object with the following properties: 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 run 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`, +`profile`, `priority` and `queue`. #### `"error"` @@ -1197,6 +1204,7 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag [Name server]: #name-server +[Privilege levels]: #privilege-levels +[`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 -[Privilege levels]: #privilege-levels From 3e4182428feb1adbe6d01b912a7d40ffe6889a26 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 9 Jul 2021 09:47:52 +0200 Subject: [PATCH 132/424] Clarifies the profiles --- docs/API.md | 61 ++++++++++++++++++++++++++----------------- docs/Configuration.md | 11 +++++--- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/docs/API.md b/docs/API.md index 72b1d8536..8f03acdee 100644 --- a/docs/API.md +++ b/docs/API.md @@ -227,19 +227,37 @@ 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: +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":"/profile: 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":"Internal error 009 \n", + "code":-32603 } } ``` @@ -361,7 +379,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 @@ -643,7 +662,8 @@ An object with the following properties: * `"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*, optional. (default: `"default"`). Run the tests using the given profile. +* `"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`) @@ -664,12 +684,8 @@ then the new request does not trigger a new test, but returns with the results o #### `"error"` -* If the given `profile` is not among the [available profiles], a user - error is returned. - -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> +* If the given `profile` is not among the [available profiles][Profile sections], + a user error is returned, see [profile name section][profile name]. ## API method: `test_progress` @@ -1042,7 +1058,8 @@ 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. +* `"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*][Name server] objects, optional. (default: `[]`) * `"ds_info"`: A list of [*DS info*][DS info] objects, optional. (default: `[]`) @@ -1061,13 +1078,8 @@ A *batch id*. * 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. - - -> -> TODO: List all possible error codes and describe what they mean enough for clients to know how react to them. -> +* If the given `profile` is not among the [available profiles][Profile sections], + a user error is returned, see the [profile name section][profile name]. ## API method: `get_batch_job_result` @@ -1190,7 +1202,6 @@ 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 [DS info]: #ds-info [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 @@ -1200,3 +1211,5 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 [net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 [Privilege levels]: #privilege-levels +[Profile name]: #profile-name +[Profile sections]: Configuration.md#public-profiles-and-private-profiles-sections diff --git a/docs/Configuration.md b/docs/Configuration.md index 199e7147b..36b07b835 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -257,9 +257,12 @@ running the RPCAPI daemon for the translation to work correctly. 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 @@ -374,7 +377,7 @@ Otherwise a new test request is enqueued. [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 From dfdcb0aec8508fad13d9a1ae51f8dcd5bfe9d992 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 9 Jul 2021 14:02:05 +0200 Subject: [PATCH 133/424] Adds editorial update --- docs/API.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index a57f08acf..903f471f3 100644 --- a/docs/API.md +++ b/docs/API.md @@ -658,9 +658,9 @@ An object with the following properties: A *test id*. -If a test has been run 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 +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. From c6c9c71fd3d50d3884f3bb55eb5a8e874fb562ad Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 9 Jul 2021 14:42:52 +0200 Subject: [PATCH 134/424] Clarfies the error messages --- docs/API.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 8f03acdee..33541f191 100644 --- a/docs/API.md +++ b/docs/API.md @@ -233,6 +233,10 @@ in the configuration file as specified in the Configuration document The name of a [*profile*](Architecture.md#profile). +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: @@ -261,6 +265,8 @@ with this type, it returns the following error message: } } ``` +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 @@ -1202,14 +1208,16 @@ 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. > +[Add_batch_job]: #api-method-add_batch_job [DS info]: #ds-info [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 [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag [Name server]: #name-server -[net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 -[net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 [Privilege levels]: #privilege-levels [Profile name]: #profile-name [Profile sections]: Configuration.md#public-profiles-and-private-profiles-sections +[Start_domain_test]: #api-method-start_domain_test +[net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 +[net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 From d3cbcca2ac692c48ca582a91b041dff669c3f4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 12 Jul 2021 15:55:14 +0200 Subject: [PATCH 135/424] validation error to array --- docs/API.md | 156 +++++++++++++++++------- docs/Configuration.md | 2 - lib/Zonemaster/Backend/Config.pm | 1 - lib/Zonemaster/Backend/RPCAPI.pm | 166 ++++++++++---------------- lib/Zonemaster/Backend/Validator.pm | 2 +- script/zonemaster_backend_rpcapi.psgi | 2 +- share/backend_config.ini | 1 - t/test01.t | 6 - t/test_validate_syntax.t | 86 ++++++------- t/validator.t | 1 + 10 files changed, 223 insertions(+), 200 deletions(-) diff --git a/docs/API.md b/docs/API.md index 72b1d8536..f33dd92fb 100644 --- a/docs/API.md +++ b/docs/API.md @@ -28,7 +28,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 +55,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`. @@ -139,22 +144,19 @@ Basic data type: object DS for [Delegation Signer](https://tools.ietf.org/html/rfc4034) references DNSKEY-records in the sub-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 in [dot-decimal notation] ; + - a valid IPv6 in [recommend text format for IPv6 addresses]. ### Language tag @@ -210,7 +212,7 @@ Properties: ### Non-negative integer Basic data type: number (integer) - + A non-negative integer is either zero or strictly positive. @@ -231,20 +233,6 @@ regex `/^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$/i`. 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: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": { - "message": "Invalid profile option format", - "status": "nok" - } -} -``` - - ### Progress percentage Basic data type: number (integer) @@ -295,7 +283,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"`. @@ -306,7 +294,6 @@ Basic data type: string Default database timestamp format: "Y-M-D H:M:S.ms". Example: "2017-12-18 07:56:17.156939" - ### Username Basic data type: string @@ -317,6 +304,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` @@ -482,9 +479,9 @@ Example response: #### `"params"` -An object with the property: +An object with the properties: -`"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"` @@ -501,10 +498,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` @@ -566,10 +579,9 @@ 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. #### `"result"` @@ -581,9 +593,26 @@ An object with the following properties: #### `"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` @@ -649,23 +678,56 @@ An object with the following properties: * `"priority"`: A *priority*, optional. (default: `10`) * `"queue"`: A *queue*, optional. (default: `0`) -> > 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), +If the test has been run with the same domain name within an interval of 10 minutes (hard coded), then the new request does not trigger a new test, but returns with the results of the last test #### `"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]. + + 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. @@ -806,7 +868,7 @@ 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. +* `"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. @@ -815,7 +877,7 @@ In the case of a test created with `start_domain_test`: 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. +* `"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 @@ -926,7 +988,7 @@ In order to use advanced api features such as the *batch test*, it's necessaire 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. -Add a new *user* +Add a new *user* This method requires the *administrative* *privilege level*. @@ -980,7 +1042,7 @@ Trying to add a already existing user: ``` Ommitting params: -```json +```json { "message": "username or api_key not provided to the method add_api_user\n", "code": -32603 @@ -1196,6 +1258,10 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [ISO 639-1]: https://en.wikipedia.org/wiki/ISO_639-1 [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag +[Validation error data]: #validation-error-data +[Dot-decimal notation]: https://en.wikipedia.org/wiki/Dot-decimal_notation +[Recommend text format for IPv6 addresses]: https://datatracker.ietf.org/doc/html/rfc5952 +[JSON Pointer]: https://datatracker.ietf.org/doc/html/rfc6901 [Name server]: #name-server [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 199e7147b..f06b55f69 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -380,5 +380,3 @@ Otherwise a new test request is enqueued. [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/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index ad4d26ebb..0813d82f9 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -740,7 +740,6 @@ sub _reset_LANGUAGE_locale { my ( $self ) = @_; delete $self->{_LANGUAGE_locale}; - return; } diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 6ea7273bb..c16f1de9b 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -29,6 +29,7 @@ use Zonemaster::Backend::Validator; my $zm_validator = Zonemaster::Backend::Validator->new; my %json_schemas; +my %extra_validators; my $recursor = Zonemaster::Engine::Recursor->new; sub joi { @@ -151,20 +152,27 @@ sub get_host_by_name { return \@adresses; } +sub get_data_from_parent_zone_validate_syntax { + 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} = joi->object->strict->props( domain => $zm_validator->domain_name->required ); +$extra_validators{get_data_from_parent_zone} = \&get_data_from_parent_zone_validate_syntax; 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; @@ -198,10 +206,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 => 'Domain name required' } ); } if ( $domain =~ m/[^[:ascii:]]+/ ) { @@ -212,7 +220,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 => 'The domain name is not a valid IDNA string and cannot be converted to an A-label' } ); } @@ -222,7 +230,7 @@ sub _check_domain { $domain, { status => 'nok', - message => encode_entities( "$type contains non-ascii characters and IDNA is not installed" ) + message => 'The domain name contains non-ascii characters and IDNA is not installed' } ); } @@ -233,7 +241,7 @@ sub _check_domain { $domain, { status => 'nok', - message => encode_entities( "The domain name character(s) not supported" ) + message => 'The domain name character(s) are not supported' } ); } @@ -243,117 +251,50 @@ 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 => 'The domain name or label is too long' } ); } return ( $domain, { status => 'ok', message => 'Syntax ok' } ); } -sub validate_syntax { +sub start_domain_test_validate_syntax { 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' ); - } - } - } - - 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->{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->{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' ); - } + my @errors = eval { + my @errors; 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} } ) { - return { status => 'nok', message => encode_entities( "Unrecognized profile name" ) }; + push @errors, { path => '/profile', message => 'Unknown profile' }; } } - 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' ); - } + 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' ); + } - 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 => '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('start_domain_test_validate_syntax', $@, '008'); } else { - return { status => 'ok', message => encode_entities( 'Syntax ok' ) }; + return @errors; } } @@ -362,26 +303,26 @@ $json_schemas{start_domain_test} = joi->object->strict->props( ipv4 => joi->boolean, ipv6 => joi->boolean, nameservers => joi->array->items( - $zm_validator->nameserver + # NOTE: Array items are not compiled automatically, so to enforce all properties we need to do it by hand + $zm_validator->nameserver->compile ), ds_info => joi->array->items( - $zm_validator->ds_info + $zm_validator->ds_info->compile ), 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 + queue => $zm_validator->queue, ); +$extra_validators{start_domain_test} = \&start_domain_test_validate_syntax; 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} ); @@ -688,15 +629,17 @@ sub jsonrpc_validate { } }; } - my @error = $method_schema->validate($jsonrpc_request->{params}); - if ( @error ) { + + my @error_response = $self->validate_params($jsonrpc_request->{method}, $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" + data => \@error_response } }; } @@ -704,4 +647,23 @@ sub jsonrpc_validate { return ''; } + +sub validate_params { + my ( $self, $method, $params ) = @_; + my $method_schema = $json_schemas{$method}; + + my @error_response = (); + + my @json_validation_error = $method_schema->validate( $params ); + + push @error_response, map { { message => $_->message, path => $_->path } } @json_validation_error; + + # Add messages from extra validation function + if ( $extra_validators{$method} ) { + my $sub_validator = $extra_validators{$method}; + push @error_response, $self->$sub_validator($params); + } + return @error_response; +} + 1; diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 2ab5b79c6..6f7b6541f 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -58,7 +58,7 @@ our %EXPORT_TAGS = ( Readonly my $IPV4_RE => qr/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/; # 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-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})?$/i; +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; diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 418687800..3ce397708 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -157,7 +157,7 @@ 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'); diff --git a/share/backend_config.ini b/share/backend_config.ini index cd5a236f3..39474fe75 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -40,4 +40,3 @@ locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE [PRIVATE PROFILES] #example_profile_2=/example/directory/test2_profile.json - diff --git a/t/test01.t b/t/test01.t index 3c73994dc..26269b49c 100644 --- a/t/test01.t +++ b/t/test01.t @@ -135,12 +135,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); diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 95e19b2bb..08a78d13f 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -8,6 +8,7 @@ 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; @@ -44,7 +45,7 @@ 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' } @@ -54,7 +55,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest encode_utf8( 'idn domain=[é]' ) => sub { - my $res = $engine->validate_syntax( + my $res = $engine->start_domain_test_validate_syntax( { %$frontend_params, domain => 'é' } @@ -65,7 +66,7 @@ subtest 'Everything but NoWarnings' => sub { }; subtest encode_utf8( 'idn domain=[éé]' ) => sub { - my $res = $engine->validate_syntax( + my $res = $engine->start_domain_test_validate_syntax( { %$frontend_params, domain => 'éé' } @@ -76,7 +77,7 @@ subtest 'Everything but NoWarnings' => sub { }; 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' } @@ -88,7 +89,7 @@ subtest 'Everything but NoWarnings' => sub { }; 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.' } @@ -100,7 +101,7 @@ subtest 'Everything but NoWarnings' => sub { }; 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' } @@ -112,7 +113,7 @@ subtest 'Everything but NoWarnings' => sub { }; 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' } @@ -123,7 +124,7 @@ subtest 'Everything but NoWarnings' => sub { }; 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' } @@ -139,80 +140,80 @@ 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( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', '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( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[é]' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[éé]' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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', + $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( '253 characters long domain without dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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 )->{status}, 'ok', encode_utf8( '254 characters long domain with trailing dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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', + $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( '254 characters long domain without trailing dot' ) - ) or diag( $engine->validate_syntax( $frontend_params )->{message} ); + ) or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 63 characters long domain label $frontend_params->{nameservers}->[0]->{ns} = '012345678901234567890123456789012345678901234567890123456789-63.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'ok', + is( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( '63 characters long domain label' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 64 characters long domain label $frontend_params->{nameservers}->[0]->{ns} = '012345678901234567890123456789012345678901234567890123456789-64-.fr'; - is( $engine->validate_syntax( $frontend_params )->{status}, 'nok', + is( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( '64 characters long domain label' ) ) - or diag( $engine->validate_syntax( $frontend_params )->{message} ); + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'delegated domain exists' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # 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( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid IPV4' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); $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} ); + is( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV4' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); $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 )->{status}, 'ok', encode_utf8( 'Valid IPV6' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); $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} ); + is( $engine->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV6' ) ) + or diag( $engine->start_domain_test_validate_syntax( $frontend_params )->{message} ); # DS $frontend_params->{domain} = 'afnic.fr'; @@ -221,21 +222,24 @@ 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( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 0, encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $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( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid Algorithm Type' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $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( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid digest length' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $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( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid digest format' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); }; diff --git a/t/validator.t b/t/validator.t index 07423fdba..0e8ad36ec 100644 --- a/t/validator.t +++ b/t/validator.t @@ -58,6 +58,7 @@ subtest 'Everything but NoWarnings' => sub { 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'; From dd09207b2d9dccd5a0a4ded68890e56edc6f4937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 12 Jul 2021 16:03:04 +0200 Subject: [PATCH 136/424] use anonymous function --- lib/Zonemaster/Backend/RPCAPI.pm | 12 ++-- t/test_validate_syntax.t | 114 +++++++++++++++---------------- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index c16f1de9b..4153cd14e 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -29,7 +29,7 @@ use Zonemaster::Backend::Validator; my $zm_validator = Zonemaster::Backend::Validator->new; my %json_schemas; -my %extra_validators; +our %extra_validators; my $recursor = Zonemaster::Engine::Recursor->new; sub joi { @@ -152,7 +152,7 @@ sub get_host_by_name { return \@adresses; } -sub get_data_from_parent_zone_validate_syntax { +$extra_validators{get_data_from_parent_zone} = sub { my ( $self, $syntax_input ) = @_; my @errors; @@ -161,11 +161,10 @@ sub get_data_from_parent_zone_validate_syntax { push @errors, { path => "/domain", message => $dn_syntax->{message} } if ( $dn_syntax->{status} eq 'nok' ); } return @errors; -} +}; $json_schemas{get_data_from_parent_zone} = joi->object->strict->props( domain => $zm_validator->domain_name->required ); -$extra_validators{get_data_from_parent_zone} = \&get_data_from_parent_zone_validate_syntax; sub get_data_from_parent_zone { my ( $self, $params ) = @_; @@ -257,7 +256,7 @@ sub _check_domain { return ( $domain, { status => 'ok', message => 'Syntax ok' } ); } -sub start_domain_test_validate_syntax { +$extra_validators{start_domain_test} = sub { my ( $self, $syntax_input ) = @_; my @errors = eval { @@ -296,7 +295,7 @@ sub start_domain_test_validate_syntax { else { return @errors; } -} +}; $json_schemas{start_domain_test} = joi->object->strict->props( domain => $zm_validator->domain_name->required, @@ -316,7 +315,6 @@ $json_schemas{start_domain_test} = joi->object->strict->props( priority => $zm_validator->priority, queue => $zm_validator->queue, ); -$extra_validators{start_domain_test} = \&start_domain_test_validate_syntax; sub start_domain_test { my ( $self, $params ) = @_; diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 08a78d13f..5ddbef26b 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -30,6 +30,8 @@ my $engine = Zonemaster::Backend::RPCAPI->new( } ); +my $start_domain_test_validate_syntax = $Zonemaster::Backend::RPCAPI::extra_validators{start_domain_test}; + subtest 'Everything but NoWarnings' => sub { my $can_use_threads = eval 'use threads; 1'; @@ -45,93 +47,90 @@ subtest 'Everything but NoWarnings' => sub { ]; subtest 'domain present' => sub { - my $res = $engine->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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 @@ -140,80 +139,81 @@ subtest 'Everything but NoWarnings' => sub { # domain present? $frontend_params->{nameservers}->[0]->{ns} = 'afnic.fr'; - is( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[é]' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'idn domain=[éé]' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', - encode_utf8( '63 characters long domain label' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_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->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'delegated domain exists' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid IPV4' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV4' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'ok', encode_utf8( 'Valid IPV6' ) ) - or diag( $engine->start_domain_test_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->start_domain_test_validate_syntax( $frontend_params )->{status}, 'nok', encode_utf8( 'Invalid IPV6' ) ) - or diag( $engine->start_domain_test_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'; @@ -225,21 +225,21 @@ subtest 'Everything but NoWarnings' => sub { $frontend_params->{ds_info}->[0]->{digtype} = 1; $frontend_params->{ds_info}->[0]->{keytag} = 5000; - is( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 0, encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) + is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 0, encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 'a'; $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; - is( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid Algorithm Type' ) ) + is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid Algorithm Type' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = '01234567890123456789012345678901234567890'; - is( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid digest length' ) ) + is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest length' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = 'Z123456789012345678901234567890123456789'; - is( @{$engine->validate_params( "start_domain_test", $frontend_params )}, 1, encode_utf8( 'Invalid digest format' ) ) + is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest format' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); }; From 589fa8b4962171bffcb374c32e80480b47f5155a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 13 Jul 2021 10:08:31 +0200 Subject: [PATCH 137/424] update doc --- docs/API.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/API.md b/docs/API.md index 0e69abd14..dc24263e8 100644 --- a/docs/API.md +++ b/docs/API.md @@ -155,8 +155,8 @@ Properties: Basic data type: string This parameter is a string that is either - - a valid IPv4 in [dot-decimal notation] ; - - a valid IPv6 in [recommend text format for IPv6 addresses]. + - a valid IPv4 address in [dot-decimal notation] ; + - a valid IPv6 address in [recommend text format for IPv6 addresses]. ### Language tag @@ -248,7 +248,12 @@ type, it returns the following error message: "id":1, "error":{ "message":"Invalid method parameter(s).", - "data":"/profile: String does not match (?^ui:^[a-z0-9]$|^[a-z0-9][a-z0-9_-]{0,30}[a-z0-9]$).", + "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" } } @@ -262,8 +267,14 @@ with this type, it returns the following error message: "jsonrpc":"2.0", "id":1, "error":{ - "message":"Internal error 009 \n", - "code":-32603 + "message":"Invalid method parameter(s).", + "data": [ + { + "path": "/profile", + "message": "Unknown profile" + }, + ], + "code":"-32602" } } ``` From 2b423aceda5ed4ecf2c93a845dfde085b360416c Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 13 Jul 2021 14:17:53 +0200 Subject: [PATCH 138/424] Updates API.md * Clarifies and corrects the section on severity level data type. * Clarifies and updates definition of property overall_result. --- docs/API.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/API.md b/docs/API.md index fed278ea8..78ff42bf5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -290,13 +290,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 @@ -931,15 +938,16 @@ 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"`. #### `"error"` @@ -1229,3 +1237,4 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [Start_domain_test]: #api-method-start_domain_test [net.ipv4]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv4 [net.ipv6]: https://metacpan.org/pod/Zonemaster::Engine::Profile#net.ipv6 +[Severity Level Definitions]: https://github.com/zonemaster/zonemaster/blob/master/docs/specifications/tests/SeverityLevelDefinitions.md From 6ce8baaf65372e9639587d0cb22c4e7e04ae3694 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 13 Jul 2021 14:22:41 +0200 Subject: [PATCH 139/424] Makes MySQL and SQLite match specification when it comes to overall_result. --- lib/Zonemaster/Backend/DB/MySQL.pm | 2 +- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index e9cb2b2b0..fca10b997 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -310,7 +310,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; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 94ae2cbcd..3f161632e 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -305,7 +305,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; From 27c4f21734a1e4d7c99f4cc14c17411cf784c71b Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 10:15:23 +0200 Subject: [PATCH 140/424] Adds API methods add_batch_job and get_batch_job_result --- script/zmb | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/script/zmb b/script/zmb index 404fa0a6e..ae98866de 100755 --- a/script/zmb +++ b/script/zmb @@ -245,6 +245,8 @@ sub cmd_start_domain_test { } + + =head2 test_progress zmb [GLOBAL OPTIONS] test_progress [OPTIONS] @@ -422,6 +424,155 @@ sub cmd_add_api_user { } +=head2 add_batch_job + + zmb [GLOBAL OPTIONS] add_api_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] add_api_user [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.', From e903cab0c143ff0e1c742c864be0c59994cb2dfa Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:42:18 +0200 Subject: [PATCH 141/424] Clarifies "delegated" and "undelegated" in connection to historic tests --- docs/API.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index fed278ea8..99ce78277 100644 --- a/docs/API.md +++ b/docs/API.md @@ -136,7 +136,7 @@ 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). @@ -910,6 +910,11 @@ 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"` @@ -1217,15 +1222,16 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [Add_batch_job]: #api-method-add_batch_job [DS info]: #ds-info +[Delegation Signer]: https://datatracker.ietf.org/doc/html/rfc4034#section-5 [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 [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag [Name server]: #name-server [Privilege levels]: #privilege-levels -[`age_reuse_previous_test`]: Configuration.md#age_reuse_previous_test [Profile name]: #profile-name [Profile sections]: Configuration.md#public-profiles-and-private-profiles-sections [Start_domain_test]: #api-method-start_domain_test +[`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 From 9749b33fc37285b183f63bc1cae2e668ba93f135 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:44:11 +0200 Subject: [PATCH 142/424] Creates method "undelegated" to be used by alla database engines --- lib/Zonemaster/Backend/DB.pm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index d108b77da..36f5e5984 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -274,6 +274,27 @@ sub generate_fingerprint { 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 normalization. + +=cut + +sub undelegated { + my ( $self, $params ) = @_; + + my $normalized_params = $self->_normalize_params( $params ); + + return 1 if defined( $$normalized_params{ds_info}[0] ); + return 1 if defined( $$normalized_params{nameservers}[0] ); + return 0; +} + + + no Moose::Role; 1; From 9b3319473d7d2dc0db7fb3c20e34c9537ea1036e Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:45:44 +0200 Subject: [PATCH 143/424] Corrects the support of delegated/undelegated in SQLite --- lib/Zonemaster/Backend/DB/SQLite.pm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 94ae2cbcd..c97214078 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -175,6 +175,7 @@ sub create_new_test { my $fingerprint = $self->generate_fingerprint( $test_params ); my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); my $result_id; @@ -212,7 +213,7 @@ sub create_new_test { $fingerprint, $encoded_params, $test_params->{domain}, - ($test_params->{nameservers})?(1):(0), + $undelegated, ); $result_id = $hash_id; } @@ -277,9 +278,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} ); @@ -342,14 +343,15 @@ sub add_batch_job { 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, params_deterministic_hash, params, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $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 ); - $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); + $sth->execute( substr(md5_hex(time().rand()), 0, 16), $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)" ); From b7419920fd04cefe7d0bc9d0d3d0f2233681009c Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:46:00 +0200 Subject: [PATCH 144/424] Corrects the support of delegated/undelegated in MySQL --- lib/Zonemaster/Backend/DB/MySQL.pm | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index e9cb2b2b0..4313799d2 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -159,6 +159,7 @@ sub create_new_test { my $fingerprint = $self->generate_fingerprint( $test_params ); my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); my $result_id; @@ -190,7 +191,7 @@ sub create_new_test { $fingerprint, $encoded_params, $test_params->{domain}, - ($test_params->{nameservers})?(1):(0), + $undelegated, ); my ( undef, $hash_id ) = $dbh->selectrow_array( @@ -348,15 +349,16 @@ sub add_batch_job { 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 (domain, batch_id, priority, queue, params_deterministic_hash, params, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $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 ); - $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); - $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params ); + $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); + # $sth->execute( $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)" ); From b12c98f15e5be7622abd79b15efc3171f9fa81b9 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:46:56 +0200 Subject: [PATCH 145/424] Updates PostgreSQL * Adds field "undelegated" to the database schema to match the other databases. * Corrects the support for delegated/undelegated in the database engine. --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 15 +++++++++------ share/initial-postgres.sql | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 67bf6a1f5..908e2aa3c 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -170,13 +170,14 @@ sub create_new_test { my $fingerprint = $self->generate_fingerprint( $test_params ); my $encoded_params = $self->encode_params( $test_params ); + my $undelegated = $self->undelegated ( $test_params ); my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; my $sth = $dbh->prepare( " - INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params) - SELECT ?, ?, ?, ?, ? + INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params, undelegated) + SELECT ?, ?, ?, ?, ?, ? WHERE NOT EXISTS ( SELECT * FROM test_results WHERE params_deterministic_hash = ? @@ -188,6 +189,7 @@ sub create_new_test { $queue_label, $fingerprint, $encoded_params, + $undelegated, $fingerprint, sprintf( "%d seconds", $seconds_between_tests_with_same_params ), ); @@ -252,9 +254,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 @results; @@ -319,14 +321,15 @@ sub add_batch_job { $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(batch_id, priority, queue, params_deterministic_hash, params, undelegated) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $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 ); - $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\n"); + $dbh->pg_putcopydata("$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)" ); diff --git a/share/initial-postgres.sql b/share/initial-postgres.sql index 4852f0666..e7a8bc812 100644 --- a/share/initial-postgres.sql +++ b/share/initial-postgres.sql @@ -16,6 +16,7 @@ CREATE TABLE test_results ( progress integer DEFAULT 0, params_deterministic_hash varchar(32), params json NOT NULL, + undelegated boolean NOT NULL DEFAULT false, results json, nb_retries integer NOT NULL DEFAULT 0 ); From 80fbbed91fba6beb656b569aa00fd5878f026394 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:50:11 +0200 Subject: [PATCH 146/424] Adds support for upgrading a PostgreSQL database to the new schema --- README.md | 22 +++++++------ ...upgrade_db_zonemaster_backend_ver_7.0.0.md | 32 +++++++++++++++++++ ...tgresql_db_zonemaster_backend_ver_7.0.0.pl | 27 ++++++++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 docs/upgrade_db_zonemaster_backend_ver_7.0.0.md create mode 100644 share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl diff --git a/README.md b/README.md index 25565d31a..f0c8b7411 100644 --- a/README.md +++ b/README.md @@ -28,21 +28,22 @@ instructions](https://github.com/zonemaster/zonemaster-engine/blob/master/docs/I ### 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 +(SQLite, MySQL/MariaDB or PostgreSQL) 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. 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 +Created by Zonemaster-Backend 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 +At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | For PostgreSQL only -If the database was created before Zonemaster-Backend version 5.0.0, then you -have to upgrade in several steps. +If the database was created before Zonemaster-Backend version 5.0.2, then you +have to upgrade it 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 @@ -87,3 +88,4 @@ The software is released under the 2-clause BSD license. See separate [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 to 7.0.0]: docs/upgrade_db_zonemaster_backend_ver_7.0.0.md diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md new file mode 100644 index 000000000..7773e9642 --- /dev/null +++ b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md @@ -0,0 +1,32 @@ +If your zonemaster database was created by a Zonemaster-Backend version smaller +than v7.0.0, and not upgraded, use the instructions in this file. + +### FreeBSD + +If the installation is on FreeBSD, then set the environment before running any +of the commands below: + +```sh +export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config.ini" +``` + +### SQLite + +No patching (upgrading) is needed on zonemaster database on SQLite for this +version of Zonemaster-Backend. + + +### MySQL (or MariaDB) + +No patching (upgrading) is needed on zonemaster database on MySQL (or MariaDB) +for this version of Zonemaster-Backend. + + +### PostgreSQL + +Run +```sh +cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +perl patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +``` + diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl new file mode 100644 index 000000000..bd8dea9a5 --- /dev/null +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -0,0 +1,27 @@ +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 PostgreSQL backend"; +} +my $dbh = Zonemaster::Backend::DB::PostgreSQL->from_config( $config )->dbh; + +sub patch_db { + + #################################################################### + # TEST RESULTS + #################################################################### + $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated boolean NOT NULL DEFAULT false' ); +} + +patch_db(); + From 4b86367b5478534752ce881b2445cce28cb8faa9 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 14:57:56 +0200 Subject: [PATCH 147/424] Adds files to MANIFEST --- MANIFEST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST b/MANIFEST index 1d793cf14..28b38eb1a 100644 --- a/MANIFEST +++ b/MANIFEST @@ -11,6 +11,7 @@ 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_db_zonemaster_backend_ver_7.0.0.md inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm @@ -57,6 +58,7 @@ 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_postgresql_db_zonemaster_backend_ver_7.0.0.pl share/tmpfiles.conf share/travis_mysql_backend_config.ini share/travis_postgresql_backend_config.ini From 37a371b195058a4105accea5ede3db105a64f0d5 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 14 Jul 2021 17:14:52 +0200 Subject: [PATCH 148/424] Changes undelegated column to integer in PostgreSQL --- share/initial-mysql.sql | 2 +- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/initial-mysql.sql b/share/initial-mysql.sql index 3776f34db..075520f3b 100644 --- a/share/initial-mysql.sql +++ b/share/initial-mysql.sql @@ -18,7 +18,7 @@ CREATE TABLE test_results ( params_deterministic_hash character varying(32), params blob NOT NULL, results mediumblob DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, + undelegated integer NOT NULL DEFAULT 0, nb_retries integer NOT NULL DEFAULT 0 ) Engine=InnoDB; diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index bd8dea9a5..d08e2919b 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -20,7 +20,7 @@ sub patch_db { #################################################################### # TEST RESULTS #################################################################### - $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated boolean NOT NULL DEFAULT false' ); + $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); } patch_db(); From 99f95364d70e2dce3bdb1057ecbfd79bd9031b36 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 19 Jul 2021 09:01:55 +0200 Subject: [PATCH 149/424] Corrects the data type of Undelegated collumn in creation script --- share/initial-postgres.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/initial-postgres.sql b/share/initial-postgres.sql index e7a8bc812..f28d17f9f 100644 --- a/share/initial-postgres.sql +++ b/share/initial-postgres.sql @@ -16,7 +16,7 @@ CREATE TABLE test_results ( progress integer DEFAULT 0, params_deterministic_hash varchar(32), params json NOT NULL, - undelegated boolean NOT NULL DEFAULT false, + undelegated integer NOT NULL DEFAULT 0, results json, nb_retries integer NOT NULL DEFAULT 0 ); From 75b30eb334f4fc568134d059e988a3ea1f10f685 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 19 Jul 2021 09:06:26 +0200 Subject: [PATCH 150/424] Corrects typo in POD text --- script/zmb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/zmb b/script/zmb index ae98866de..19613feb7 100755 --- a/script/zmb +++ b/script/zmb @@ -426,7 +426,7 @@ sub cmd_add_api_user { =head2 add_batch_job - zmb [GLOBAL OPTIONS] add_api_job [OPTIONS] + zmb [GLOBAL OPTIONS] add_batch_job [OPTIONS] Options: --username USERNAME @@ -547,7 +547,7 @@ sub cmd_add_batch_job { =head2 get_batch_job_result - zmb [GLOBAL OPTIONS] add_api_user [OPTIONS] + zmb [GLOBAL OPTIONS] get_batch_job_result [OPTIONS] Options: --batch-id BATCH-ID From 138e24907817a45784436c99735b912cc4311792 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 20 Jul 2021 12:04:47 +0200 Subject: [PATCH 151/424] Updates script for initialize PostgreSQL database to match the regular initialization script --- script/create_db_postgresql_9.3.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 0778252f6..fa5bca842 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -51,6 +51,7 @@ sub create_db { progress integer DEFAULT 0, params_deterministic_hash character varying(32), params json NOT NULL, + undelegated integer NOT NULL DEFAULT 0, results json DEFAULT NULL, nb_retries integer NOT NULL DEFAULT 0 ) From 5ce0dc379c024875def37d83901fae3d4f32ae1c Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 21 Jul 2021 15:27:54 +0200 Subject: [PATCH 152/424] Adds test of filter in get_test_history --- t/test01.t | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/t/test01.t b/t/test01.t index 3c73994dc..013c527e6 100644 --- a/t/test01.t +++ b/t/test01.t @@ -191,6 +191,90 @@ subtest 'mock another client' => sub { }; +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 ); } From 49955e4993703effeb734a21ee51e4277a58fd50 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 21 Jul 2021 16:08:42 +0200 Subject: [PATCH 153/424] Updates data file --- t/test01.data | 220 +++++++++++++++++++++++++------------------------- 1 file changed, 109 insertions(+), 111 deletions(-) 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 {} From 71ba7d1d7aefd0d37dd640b97722b4392554018a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 26 Jul 2021 12:01:09 +0200 Subject: [PATCH 154/424] Remove queue and priority from fingerprint Those parameters do not affect the outcome, so there is no need to keep them in the fingerprint computation. --- lib/Zonemaster/Backend/DB.pm | 2 -- t/db.t | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 36f5e5984..3020a7978 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -196,8 +196,6 @@ sub _normalize_params { $normalized{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); $normalized{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); $normalized{profile} = $$params{profile} // "default"; - $normalized{priority} = $$params{priority} // 10; - $normalized{queue} = $$params{queue} // 0; my $array_ds_info = $$params{ds_info} // []; my @array_ds_info_sort = sort { diff --git a/t/db.t b/t/db.t index 30da86ffa..b78b9a9c5 100644 --- a/t/db.t +++ b/t/db.t @@ -19,7 +19,7 @@ sub encode_and_fingerprint { subtest 'encoding and fingerprint' => sub { subtest 'missing properties' => sub { - my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; + my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default"}'; my %params = ( domain => "example.com" ); @@ -120,7 +120,7 @@ subtest 'encoding and fingerprint' => sub { }; subtest 'garbage properties set' => sub { - my $expected_encoded_params = '{"client":"GUI v3.3.0","domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"priority":10,"profile":"default","queue":0}'; + 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", ); From b13c247b6ed10e30c862f24f119e3814caee9a82 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 26 Jul 2021 12:07:09 +0200 Subject: [PATCH 155/424] Update documentation --- docs/API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 99ce78277..377dfea64 100644 --- a/docs/API.md +++ b/docs/API.md @@ -691,8 +691,8 @@ the `test id` of the previous test will be returned. The default value of 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`, -`profile`, `priority` and `queue`. +considered to be the same are `domain`, `ipv6`, `ipv4`, `nameservers`, `ds_info` +and `profile`. #### `"error"` From 2603d22eb766f7670f687066dce1d782d736054b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 26 Jul 2021 15:38:12 +0200 Subject: [PATCH 156/424] Remove comment with duplicated code This code was wrongly added in 62038f5 and commented in b741992. It's time to definitely remove it. --- lib/Zonemaster/Backend/DB/MySQL.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 4313799d2..2419ccbb4 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -358,7 +358,6 @@ sub add_batch_job { my $undelegated = $self->undelegated ( $test_params ); $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); - # $sth->execute( $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)" ); From 54184ca01a22e087778d2dd8f7590253790f5d59 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 13 Jul 2021 11:29:30 +0200 Subject: [PATCH 157/424] Adds table of contents to API.md --- docs/API.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/API.md b/docs/API.md index 536ec3774..3839fd65a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,5 +1,49 @@ # 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) +* [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*. From 671e9108d6679963c8fe17f450b8db10ef416b0f Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 13 Jul 2021 11:41:57 +0200 Subject: [PATCH 158/424] Adds table of contents to Configuration.md --- docs/Configuration.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/Configuration.md b/docs/Configuration.md index 36b07b835..cb51eabe2 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,5 +1,42 @@ # Configuration +## Table of contents + +* [Introduction](#Introduction) +* [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) + * [password](#password) + * [database](#database) +* [POSTGRESQL section](#POSTGRESQL-section) + * [host](#host) + * [port](#port) + * [user](#user) + * [password](#password) + * [database](#database) +* [SQLITE section](#SQLITE-section) + * [database_file](#database_file) +* [LANGUAGE section](#LANGUAGE-section) + * [locale](#locale) +* [PUBLIC PROFILES and PRIVATE PROFILES sections](#PUBLIC-PROFILES-and-PRIVATE-PROFILES-sections) +* [ZONEMASTER section](#ZONEMASTER-section) + * [max_zonemaster_execution_time](#max_zonemaster_execution_time) + * [maximal_number_of_retries](#maximal_number_of_retries) + * [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 `/usr/local/etc/zonemaster/backend_config.ini` (FreeBSD). Following From 6a1e5b287c1471fe6198fda460a3c1053a4aa061 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 27 Jul 2021 11:10:37 +0200 Subject: [PATCH 159/424] Corrects level of headline and adds missing entry --- docs/API.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 3839fd65a..57956ee68 100644 --- a/docs/API.md +++ b/docs/API.md @@ -38,6 +38,7 @@ * [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) @@ -961,7 +962,7 @@ Example response: > symbol. > -## Undelegated and delegated +### 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"` From 76a79d41418639d9df80cbeaaf7fccdb28162e83 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 27 Jul 2021 11:17:35 +0200 Subject: [PATCH 160/424] Corrects incorrect links --- docs/Configuration.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index cb51eabe2..2b62a56a2 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -13,15 +13,15 @@ * [MYSQL section](#MYSQL-section) * [host](#host) * [port](#port) - * [user](#user) - * [password](#password) + * [user](#user-1) + * [password](#password-1) * [database](#database) * [POSTGRESQL section](#POSTGRESQL-section) - * [host](#host) - * [port](#port) - * [user](#user) - * [password](#password) - * [database](#database) + * [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) From 8cc5186dab68adb94929a676b951e280b72caab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 28 Jul 2021 13:08:55 +0200 Subject: [PATCH 161/424] add scripts to backfill undelegated fields in db --- ...tgresql_db_zonemaster_backend_ver_7.0.0.pl | 52 ++++++++++++++++++- ..._sqlite_db_zonemaster_backend_ver_7.0.0.pl | 34 ++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index d08e2919b..d4d3ad250 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -20,8 +20,56 @@ sub patch_db { #################################################################### # TEST RESULTS #################################################################### - $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); + $dbh->do( 'ALTER TABLE test_results ADD COLUMN IF NOT EXISTS undelegated integer NOT NULL DEFAULT 0' ); + $dbh->do( qq[ +update test_results set undelegated = test_results_undelegated.undelegated_bool::int +from ( + select + test_results.id, + ( + case when ds_filled.ds_filled is null then false else ds_filled.ds_filled end + or + case when ns_filled.ns_filled is null then false else ns_filled.ns_filled end + ) as undelegated_bool + from test_results + left join ( + select + count(*) > 0 as ds_filled, + id + from ( + select + jd.value, + id + from + ( + select json_array_elements(params->'ds_info') as ja, id + from test_results + ) as s1, + json_each_text(ja) as jd + ) as s2 + where value is not null and value::text != ''::text + group by id + ) as ds_filled on ds_filled.id = test_results.id + left join ( + select + count(*) > 0 as ns_filled, + id + from ( + select + jd.value, + id + from + ( + select json_array_elements(params->'nameservers') as ja, id + from test_results + ) as s1, + json_each_text(ja) as jd + ) as s2 + where value is not null and value::text != ''::text + group by id + ) as ns_filled on ns_filled.id = test_results.id +) as test_results_undelegated where test_results.id = test_results_undelegated.id; + ] ); } patch_db(); - diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl new file mode 100644 index 000000000..2ed85c122 --- /dev/null +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -0,0 +1,34 @@ +use strict; +use warnings; +use utf8; +use Data::Dumper; +use Encode; +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 { + my @arefs = $dbh->selectall_array('SELECT id, params from test_results', undef); + foreach my $row (@arefs) { + my $id = @$row[0]; + my $raw_params = decode_json(@$row[1]); + my $ds_info_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); + my $nameservers_values = scalar( map { grep (!/^$/, 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); + } +} + +patch_db(); From eab5dfb6d5f2aaf6e1f6a8924e006ea38c6af1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 28 Jul 2021 13:49:45 +0200 Subject: [PATCH 162/424] make the script compatible with PG 9.3 --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index d4d3ad250..73a46eecd 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -20,7 +20,11 @@ sub patch_db { #################################################################### # TEST RESULTS #################################################################### - $dbh->do( 'ALTER TABLE test_results ADD COLUMN IF NOT EXISTS undelegated integer NOT NULL DEFAULT 0' ); + eval { + $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); + }; + print "Error while changing DB schema: " . $@; + $dbh->do( qq[ update test_results set undelegated = test_results_undelegated.undelegated_bool::int from ( From 9c7f8950614e080e0722cde0213892e14cef8636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 28 Jul 2021 15:19:53 +0200 Subject: [PATCH 163/424] add mysql patch script --- ...h_mysql_db_zonemaster_backend_ver_7.0.0.pl | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl new file mode 100644 index 000000000..1cf5b79db --- /dev/null +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -0,0 +1,34 @@ +use strict; +use warnings; +use utf8; +use Data::Dumper; +use Encode; +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 { + my @arefs = $dbh->selectall_array('SELECT id, params from test_results', undef); + foreach my $row (@arefs) { + my $id = @$row[0]; + my $raw_params = decode_json(@$row[1]); + my $ds_info_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); + my $nameservers_values = scalar( map { grep (!/^$/, 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); + } +} + +patch_db(); From f9b56ef33d7c84e2238f7b6c776c843edd149bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 29 Jul 2021 10:36:12 +0200 Subject: [PATCH 164/424] update manifest --- MANIFEST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST b/MANIFEST index 28b38eb1a..fdeca7b03 100644 --- a/MANIFEST +++ b/MANIFEST @@ -56,9 +56,11 @@ 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_mysql_db_zonemaster_backend_ver_7.0.0.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_postgresql_db_zonemaster_backend_ver_7.0.0.pl +share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl share/tmpfiles.conf share/travis_mysql_backend_config.ini share/travis_postgresql_backend_config.ini From 17c12a0b59bef591b398cc651837b6b6dc4f7c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 29 Jul 2021 13:11:49 +0200 Subject: [PATCH 165/424] improve config search and add default freebsd config location --- lib/Zonemaster/Backend/Config.pm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index ad4d26ebb..19c1329d8 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -16,14 +16,23 @@ use Zonemaster::Backend::Validator qw( :untaint ); use Zonemaster::Backend::DB; our $path; + +my @seatch_paths = ( + '/etc/zonemaster/backend_config.ini', + '/usr/local/etc/zonemaster/backend_config.ini', + dist_file('Zonemaster-Backend', "backend_config.ini") +); + 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"); + for my $default_path (@seatch_paths) { + if ( -e $default_path ) { + $path = $default_path; + last; + } + } } Readonly my @SIG_NAME => split ' ', $Config{sig_name}; From f7074132d108d628b6d86941f8e22e8f1bba1a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 29 Jul 2021 13:18:02 +0200 Subject: [PATCH 166/424] fix typo --- lib/Zonemaster/Backend/Config.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 19c1329d8..53bdbca7a 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -17,7 +17,7 @@ use Zonemaster::Backend::DB; our $path; -my @seatch_paths = ( +my @search_paths = ( '/etc/zonemaster/backend_config.ini', '/usr/local/etc/zonemaster/backend_config.ini', dist_file('Zonemaster-Backend', "backend_config.ini") @@ -27,7 +27,7 @@ if ($ENV{ZONEMASTER_BACKEND_CONFIG_FILE}) { $path = $ENV{ZONEMASTER_BACKEND_CONFIG_FILE}; } else { - for my $default_path (@seatch_paths) { + for my $default_path (@search_paths) { if ( -e $default_path ) { $path = $default_path; last; From 3c2623de330d7df2a9f2f7789dd9a14b24df8f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 29 Jul 2021 15:21:46 +0200 Subject: [PATCH 167/424] update travis --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 97051215c..dd0afc76f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -107,6 +107,10 @@ 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 @@ -122,4 +126,3 @@ before_script: script: - perl Makefile.PL && make test - From d921fdf95f338a54ac3fdcfc3a6ee1591d941aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 29 Jul 2021 15:26:29 +0200 Subject: [PATCH 168/424] update docs --- README.md | 4 ++-- docs/upgrade_db_zonemaster_backend_ver_7.0.0.md | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f0c8b7411..f00db1726 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ 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 (SQLite, MySQL/MariaDB or PostgreSQL) then you should not reset the database when @@ -40,7 +40,7 @@ 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 -At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | For PostgreSQL only +At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | For all database types If the database was created before Zonemaster-Backend version 5.0.2, then you have to upgrade it in several steps. diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md index 7773e9642..4270e3761 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md @@ -12,14 +12,20 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. ### SQLite -No patching (upgrading) is needed on zonemaster database on SQLite for this -version of Zonemaster-Backend. +Run +```sh +cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +``` ### MySQL (or MariaDB) -No patching (upgrading) is needed on zonemaster database on MySQL (or MariaDB) -for this version of Zonemaster-Backend. +Run +```sh +cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +perl patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +``` ### PostgreSQL @@ -29,4 +35,3 @@ Run cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') perl patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl ``` - From 80ee2b17fad2ea8e3ee8f6b4da9f90d212ec968f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 3 Aug 2021 09:42:05 +0200 Subject: [PATCH 169/424] implement comments --- docs/API.md | 4 ++-- t/test01.t | 6 ++++++ t/test_validate_syntax.t | 11 ++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index dc24263e8..0c3224524 100644 --- a/docs/API.md +++ b/docs/API.md @@ -529,7 +529,7 @@ Example response: #### `"params"` -An object with the properties: +An object with the property: * `"hostname"`: A *domain name*, required. The hostname whose IP addresses are to be resolved. @@ -629,7 +629,7 @@ Example response: #### `"params"` -An object with the properties: +An object with the property: * `"domain"`: A *domain name*, required. The domain whose DNS records are requested. diff --git a/t/test01.t b/t/test01.t index 26269b49c..3c73994dc 100644 --- a/t/test01.t +++ b/t/test01.t @@ -135,6 +135,12 @@ 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); diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 5ddbef26b..09c4fc459 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -238,8 +238,17 @@ subtest 'Everything but NoWarnings' => sub { is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest length' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); - $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = 'Z123456789012345678901234567890123456789'; is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest format' ) ) or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); + + $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; + $frontend_params->{ds_info}->[0]->{digtype} = -1; + is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest type' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $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", $frontend_params ), 1, encode_utf8( 'Invalid keytag' ) ) + or diag( encode_json $engine->validate_params( "start_domain_test", $frontend_params ) ); }; From 98c467a629837de6f7d69fc56e26108b5bd8600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 3 Aug 2021 10:41:31 +0200 Subject: [PATCH 170/424] allow ipv4 mapped addresses --- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 6ea7273bb..3f1195c37 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -579,7 +579,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; From 122aa0f326035eaa49ff106180dc7feb9a44e385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 3 Aug 2021 16:47:39 +0200 Subject: [PATCH 171/424] remove unused import --- README.md | 2 +- share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 3 --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 3 --- share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 3 --- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index f00db1726..c7d3e908f 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ 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 -At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | For all database types +At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | If the database was created before Zonemaster-Backend version 5.0.2, then you have to upgrade it in several steps. diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index 1cf5b79db..822645005 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use JSON::PP; use DBI qw(:utils); diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 73a46eecd..41291b8b2 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use DBI qw(:utils); diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 2ed85c122..5647f4e12 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -1,8 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; use JSON::PP; use DBI qw(:utils); From 9cf67800e33b0036190ba7b9afda19673e446eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 4 Aug 2021 10:51:43 +0200 Subject: [PATCH 172/424] add undelegated value in results history --- docs/API.md | 18 ++++++++++-------- lib/Zonemaster/Backend/DB/MySQL.pm | 3 +++ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 ++ lib/Zonemaster/Backend/DB/SQLite.pm | 2 ++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/API.md b/docs/API.md index 57956ee68..bb690fba7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -255,7 +255,7 @@ Properties: ### Non-negative integer Basic data type: number (integer) - + A non-negative integer is either zero or strictly positive. @@ -371,7 +371,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"`. @@ -734,7 +734,7 @@ An object with the following properties: #### `"result"` -A *test id*. +A *test id*. 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 @@ -887,7 +887,7 @@ 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. +* `"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. @@ -896,7 +896,7 @@ In the case of a test created with `start_domain_test`: 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. +* `"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 @@ -944,10 +944,12 @@ Example response: { "id": "c45a3f8256c4a155", "creation_time": "2016-11-15 11:53:13.965982", + "undelegated": 1, "overall_result": "error", }, { "id": "32dd4bc0582b6bf9", + "undelegated": 0, "creation_time": "2016-11-14 08:46:41.532047", "overall_result": "error", }, @@ -1013,7 +1015,7 @@ In order to use advanced api features such as the *batch test*, it's necessaire 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. -Add a new *user* +Add a new *user* This method requires the *administrative* *privilege level*. @@ -1067,7 +1069,7 @@ Trying to add a already existing user: ``` Ommitting params: -```json +```json { "message": "username or api_key not provided to the method add_api_user\n", "code": -32603 @@ -1129,7 +1131,7 @@ 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*][profile name], optional (default: +* `"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*][Name server] objects, optional. (default: `[]`) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ef607b51f..307b617e8 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -275,6 +275,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 @@ -291,6 +292,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 @@ -321,6 +323,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall, } ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 908e2aa3c..d5b5c65bc 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -267,6 +267,7 @@ sub get_test_history { (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 @@ -291,6 +292,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall_result, } ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 7dc784e3c..6b1895d3d 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -290,6 +290,7 @@ sub get_test_history { hash_id, creation_time, params, + undelegated, results FROM test_results @@ -314,6 +315,7 @@ sub get_test_history { { id => $h->{hash_id}, creation_time => $h->{creation_time}, + undelegated => $h->{undelegated}, overall_result => $overall, } ); From 877c8cac745e8e2061543c60ecb3528ec021dff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 5 Aug 2021 11:40:09 +0200 Subject: [PATCH 173/424] move search path definition --- lib/Zonemaster/Backend/Config.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 53bdbca7a..1658cb443 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -17,16 +17,16 @@ use Zonemaster::Backend::DB; our $path; -my @search_paths = ( - '/etc/zonemaster/backend_config.ini', - '/usr/local/etc/zonemaster/backend_config.ini', - dist_file('Zonemaster-Backend', "backend_config.ini") -); - if ($ENV{ZONEMASTER_BACKEND_CONFIG_FILE}) { $path = $ENV{ZONEMASTER_BACKEND_CONFIG_FILE}; } else { + 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; From aea3853903200ef53c5c92989c120904d6afda2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 5 Aug 2021 11:46:19 +0200 Subject: [PATCH 174/424] only print error if exists --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 41291b8b2..67571864c 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -20,7 +20,9 @@ sub patch_db { eval { $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); }; - print "Error while changing DB schema: " . $@; + if ($@) { + print "Error while changing DB schema: " . $@; + } $dbh->do( qq[ update test_results set undelegated = test_results_undelegated.undelegated_bool::int From d7b2b6092642630cf6e33d909dcfad56ef7876ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 5 Aug 2021 11:51:45 +0200 Subject: [PATCH 175/424] update doc --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index bb690fba7..04fc56351 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1000,7 +1000,7 @@ An object with the following properties: `"ERROR"`, but none with `"CRITICAL"`. * `"critical"`, if there is at least one message with *severity level* `"CRITICAL"`. - +* `"undelegated"`: `1` if the test is undelegated, `0` otherwise. #### `"error"` From 04222e04529515b5571315a1d7366968e6ce231d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 11 Aug 2021 11:56:31 +0200 Subject: [PATCH 176/424] use logger instead of warn --- lib/Zonemaster/Backend/RPCAPI.pm | 3 ++- script/zonemaster_backend_rpcapi.psgi | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 4153cd14e..efbc70e0a 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -26,6 +26,7 @@ use Zonemaster::Backend; use Zonemaster::Backend::Config; use Zonemaster::Backend::Translator; use Zonemaster::Backend::Validator; +use Log::Any qw( $log ); my $zm_validator = Zonemaster::Backend::Validator->new; my %json_schemas; @@ -77,7 +78,7 @@ sub handle_exception { $exception =~ s/\n/ /g; $exception =~ s/^\s+|\s+$//g; - warn "Internal error $exception_id: Unexpected error in the $method API call: [$exception] \n"; + $log->error("Internal error $exception_id: Unexpected error in the $method API call: [$exception]"); die "Internal error $exception_id \n"; } diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 3ce397708..bae147650 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -45,15 +45,20 @@ 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 { my $m = $_; $m =~ s/\n/ /g; $m =~ s/^\s+|\s+$//g; $m } @_); +}; + my $config = Zonemaster::Backend::Config->load_config(); builder { From fef94ea5ed1b5ac41c3483aa99e9b406cd763300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 11 Aug 2021 17:56:27 +0200 Subject: [PATCH 177/424] process dead tests --- lib/Zonemaster/Backend/DB.pm | 46 +++++++++++++++++-------- lib/Zonemaster/Backend/DB/MySQL.pm | 6 ++++ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 ++++ lib/Zonemaster/Backend/DB/SQLite.pm | 6 ++++ script/zonemaster_backend_testagent | 1 + 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 36f5e5984..6cd39598d 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -10,6 +10,7 @@ use JSON::PP; use Digest::MD5 qw(md5_hex); use Encode; use Log::Any qw( $log ); +use Time::HiRes qw[time]; use Zonemaster::Engine::Profile @@ -137,25 +138,40 @@ sub process_unfinished_tests { $self->schedule_for_retry($h->{hash_id}); } 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" => $test_run_timeout, - }; - $self->process_unfinished_tests_give_up($result, $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, $test_run_max_retries ) = @_; + my ( $nb_retries, $results ) = $self->dbh->selectrow_array("SELECT nb_retries, results FROM test_results WHERE hash_id = ?", undef, $hash_id); + if ( $nb_retries < $test_run_max_retries) { + $self->schedule_for_retry($hash_id); + } else { + $self->force_end_test($hash_id, $results, $self->get_timestamp($hash_id)); + } +} + # A thin wrapper around DBI->connect to ensure similar behavior across database # engines. sub _new_dbh { diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ef607b51f..af7b590b5 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -423,6 +423,12 @@ sub schedule_for_retry { $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); } +sub get_timestamp { + my ( $self, $hash_id ) = @_; + + return $self->dbh->selectrow_array("SELECT now() - test_start_time FROM test_results WHERE hash_id=?", undef, $hash_id); +} + no Moose; __PACKAGE__->meta()->make_immutable(); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 908e2aa3c..41ee60b04 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -395,6 +395,12 @@ sub schedule_for_retry { $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); } +sub get_timestamp { + my ( $self, $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; __PACKAGE__->meta()->make_immutable(); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 7dc784e3c..1be1bedf7 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -416,6 +416,12 @@ sub schedule_for_retry { $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); } +sub get_timestamp { + my ( $self, $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; __PACKAGE__->meta()->make_immutable(); diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index de8c20d30..b7d372c57 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -145,6 +145,7 @@ sub main { if ( $@ ) { chomp $@; $log->error( "Test died: $id: $@" ); + $self->db->process_dead_test( $id, $self->config->ZONEMASTER_maximal_number_of_retries ) } else { $log->info( "Test completed: $id" ); From 3317d809328cbc2911a19a52f312bc4c99e4143c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 11 Aug 2021 18:14:44 +0200 Subject: [PATCH 178/424] make the stdout logger match the file logger --- script/zonemaster_backend_testagent | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index de8c20d30..58b75b4f6 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -78,9 +78,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 +100,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 { my $m = $_; $m =~ s/\n/ /g; $m =~ s/^\s+|\s+$//g; $m } @_); +}; + ### ### Actual functionality ### From 51ee8f046247318d6b4385698d4cc8cc28e80ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 12 Aug 2021 09:49:39 +0200 Subject: [PATCH 179/424] lasy load rows --- share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 9 +++++---- share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index 822645005..59615b82b 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -16,10 +16,11 @@ sub patch_db { - my @arefs = $dbh->selectall_array('SELECT id, params from test_results', undef); - foreach my $row (@arefs) { - my $id = @$row[0]; - my $raw_params = decode_json(@$row[1]); + 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( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); my $nameservers_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{nameservers}}); my $undelegated = $ds_info_values > 0 || $nameservers_values > 0 || 0; diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 5647f4e12..828d5b481 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -16,10 +16,11 @@ sub patch_db { - my @arefs = $dbh->selectall_array('SELECT id, params from test_results', undef); - foreach my $row (@arefs) { - my $id = @$row[0]; - my $raw_params = decode_json(@$row[1]); + 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( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); my $nameservers_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{nameservers}}); my $undelegated = $ds_info_values > 0 || $nameservers_values > 0 || 0; From af3b17305d31694de7cdeacdab9e715886cd87b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 12 Aug 2021 14:48:37 +0200 Subject: [PATCH 180/424] add errors module --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 31 ++++-- lib/Zonemaster/Backend/Errors.pm | 141 ++++++++++++++++++++++++ lib/Zonemaster/Backend/RPCAPI.pm | 22 ++-- script/zonemaster_db_exporter.psgi | 35 ++++++ 4 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 lib/Zonemaster/Backend/Errors.pm create mode 100644 script/zonemaster_db_exporter.psgi diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 908e2aa3c..6cda68f3a 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -10,6 +10,7 @@ use Encode; use JSON::PP; use Zonemaster::Backend::DB; +use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; @@ -207,8 +208,12 @@ sub get_test_params { 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; + eval { $result = decode_json( encode_utf8( $params_json ) ); }; - die "$@ \n" if $@; + + die Zonemaster::Backend::Error::Internal->new( reason => "$@", data => { test_id => $test_id } ) if $@; return $result; } @@ -224,25 +229,35 @@ sub test_results { my $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}; + $result = $hrefs->{$test_id}; + }; + + die Zonemaster::Backend::Error::Internal->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; + eval { # 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::Internal->new( reason => "$@", data => { test_id => $test_id }) if $@; return $result; } diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm new file mode 100644 index 000000000..068e67046 --- /dev/null +++ b/lib/Zonemaster/Backend/Errors.pm @@ -0,0 +1,141 @@ +package Zonemaster::Backend::Error; +use Moose; +use Data::Dumper; + + +has 'message' => ( + is => 'rw', + isa => 'Str', + required => 1, +); + +has 'code' => ( + is => 'rw', + isa => 'Int', + required => 1, +); + +has 'data' => ( + is => 'rw', + isa => 'Any', + default => undef, +); + +sub as_hash { + my $self = shift; + my $error = { + code => $self->code, + message => $self->message + }; + $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 => 'rw', + initializer => 'reason', +); + +has 'method' => ( + is => 'rw', + isa => 'Str', + builder => '_build_method' +); + +has 'id' => ( + is => 'rw', + isa => 'Int', + default => 0, +); + +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 'reason' => sub { + my $orig = shift; + my $self = shift; + + my ( $value, $setter, $attr ) = @_; + + # reader + return $self->$orig if not $value; + + # trim new lines + $value =~ s/\n/ /g; + $value =~ s/^\s+|\s+$//g; + + # initializer + return $setter->($value) if $setter; + + # writer + $self->$orig($value); +}; + + +sub as_string { + my $self = shift; + my $str = sprintf "Internal error %0.3d: Unexpected error in the `%s` method: [%s]", $self->id, $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 +); + +1; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index efbc70e0a..8245c3783 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -26,7 +26,7 @@ use Zonemaster::Backend; use Zonemaster::Backend::Config; use Zonemaster::Backend::Translator; use Zonemaster::Backend::Validator; -use Log::Any qw( $log ); +use Zonemaster::Backend::Errors; my $zm_validator = Zonemaster::Backend::Validator->new; my %json_schemas; @@ -68,18 +68,26 @@ sub _init_db { 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'); + if ($@) { } } sub handle_exception { - my ( $method, $exception, $exception_id ) = @_; + my ( $_method, $exception, $exception_id ) = @_; + + if ( !$exception->isa('Zonemaster::Backend::Error') ) { + my $reason = $exception; + $exception = Zonemaster::Backend::Error::Internal->new(reason => $reason, id => $exception_id); + } + + if ( $exception->isa('Zonemaster::Backend::Error::Internal') ) { + $log->error($exception->as_string); + } else { + $log->notice($exception->as_string); + } - $exception =~ s/\n/ /g; - $exception =~ s/^\s+|\s+$//g; - $log->error("Internal error $exception_id: Unexpected error in the $method API call: [$exception]"); - die "Internal error $exception_id \n"; + die $exception->as_hash; } $json_schemas{version_info} = joi->object->strict; diff --git a/script/zonemaster_db_exporter.psgi b/script/zonemaster_db_exporter.psgi new file mode 100644 index 000000000..bb7144a2d --- /dev/null +++ b/script/zonemaster_db_exporter.psgi @@ -0,0 +1,35 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB; +use Net::Prometheus; + +my $client = Net::Prometheus->new; + +my $tests_gauge = $client->new_gauge( + name => 'zonemaster_tests_total', + help => 'Total number of tests', + labels => [ 'state' ], +); + +my $config = Zonemaster::Backend::Config->load_config(); +my $dbtype = $config->DB_engine; +my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); +my $db = $dbclass->from_config( $config ); + +my $prom_app = $client->psgi_app; + +sub { + my $queued = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 0'); + $tests_gauge->labels('queued')->set($queued->{count}); + + my $finished = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 100'); + $tests_gauge->labels('finished')->set($finished->{count}); + + my $running = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress > 0 and progress < 100'); + $tests_gauge->labels('running')->set($running->{count}); + + $prom_app->(@_); +}; From 14625325a94ba71c937ca531d6d651b08bae0f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 12 Aug 2021 15:20:28 +0200 Subject: [PATCH 181/424] improve errors --- lib/Zonemaster/Backend/DB/MySQL.pm | 21 ++++++++++++++++++--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 11 ++++------- lib/Zonemaster/Backend/DB/SQLite.pm | 22 +++++++++++++++++++--- lib/Zonemaster/Backend/Errors.pm | 23 +++++++++++++++++++++-- lib/Zonemaster/Backend/RPCAPI.pm | 3 ++- 5 files changed, 64 insertions(+), 16 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ef607b51f..473ce396d 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -10,6 +10,7 @@ use DBI qw(:utils); use JSON::PP; use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); +use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; @@ -228,12 +229,14 @@ sub get_test_params { my ( $params_json ) = $self->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 { $result = decode_json( $params_json ); }; - warn "decoding of params_json failed (testi_id: [$test_id]):".Dumper($params_json) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; return decode_json( $params_json ); } @@ -249,8 +252,20 @@ 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; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 6cda68f3a..b249b26a8 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -213,7 +213,7 @@ sub get_test_params { eval { $result = decode_json( encode_utf8( $params_json ) ); }; - die Zonemaster::Backend::Error::Internal->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; return $result; } @@ -227,12 +227,9 @@ sub test_results { if ( $results ); my $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}; - }; + 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::Internal->new( reason => "$@", data => { test_id => $test_id } ) if $@; die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; eval { @@ -257,7 +254,7 @@ sub test_results { } }; - die Zonemaster::Backend::Error::Internal->new( reason => "$@", data => { test_id => $test_id }) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id }) if $@; return $result; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 7dc784e3c..ecea51f89 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -11,6 +11,8 @@ use Digest::MD5 qw(md5_hex); use JSON::PP; use Log::Any qw( $log ); +use Zonemaster::Backend::Errors; + with 'Zonemaster::Backend::DB'; has 'dbh' => ( @@ -244,12 +246,14 @@ sub get_test_params { my ( $params_json ) = $self->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 { $result = decode_json( $params_json ); }; - $log->warn( "decoding of params_json failed (test_id: [$test_id]):".Dumper($params_json) ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; return $result; } @@ -265,8 +269,20 @@ 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; } diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index 068e67046..903579d2c 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -25,7 +25,8 @@ sub as_hash { my $self = shift; my $error = { code => $self->code, - message => $self->message + message => $self->message, + error => ref($self), }; $error->{data} = $self->data if defined $self->data; return $error; @@ -114,10 +115,23 @@ around 'reason' => sub { $self->$orig($value); }; +around 'as_hash' => sub { + my $orig = shift; + my $self = shift; + + my $href = $self->$orig; + + $href->{exception_id} = $self->id; + $href->{reason} = $self->reason; + $href->{method} = $self->method; + + return $href; +}; + sub as_string { my $self = shift; - my $str = sprintf "Internal error %0.3d: Unexpected error in the `%s` method: [%s]", $self->id, $self->method, $self->reason; + my $str = sprintf "Internal error %0.3d (%s): Unexpected error in the `%s` method: [%s]", $self->id, ref($self), $self->method, $self->reason; if (defined $self->data) { $str .= sprintf "; Context: %s", $self->_data_dump; } @@ -138,4 +152,9 @@ has '+code' => ( default => -32000 ); +package Zonemaster::Backend::Error::JsonError; +use Moose; + +extends 'Zonemaster::Backend::Error::Internal'; + 1; diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 8245c3783..9f1a32bf0 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -68,8 +68,9 @@ sub _init_db { my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); $self->{db} = $dbclass->from_config( $self->{config} ); }; - handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); + if ($@) { + handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); } } From 6484d04bca52ca1b215c55e113c1000911f04929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 12 Aug 2021 16:03:28 +0200 Subject: [PATCH 182/424] change log level --- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index ecea51f89..3568080ab 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -270,7 +270,7 @@ sub test_results { 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}; - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; + #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} ); diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 9f1a32bf0..1be68d685 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -85,7 +85,7 @@ sub handle_exception { if ( $exception->isa('Zonemaster::Backend::Error::Internal') ) { $log->error($exception->as_string); } else { - $log->notice($exception->as_string); + $log->info($exception->as_string); } die $exception->as_hash; From f9d0d8d2ef43f669e07f7e4f9d3ec1c8d39591f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 12 Aug 2021 16:07:55 +0200 Subject: [PATCH 183/424] fix comment --- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 3568080ab..ecea51f89 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -270,7 +270,7 @@ sub test_results { 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}; - #die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; + 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} ); From 4e8ea763f41051383a817d53e2bbf3445ae70134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 09:52:04 +0200 Subject: [PATCH 184/424] remove special instructions --- docs/upgrade_db_zonemaster_backend_ver_7.0.0.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md index 4270e3761..fd4b9d592 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md @@ -1,15 +1,6 @@ If your zonemaster database was created by a Zonemaster-Backend version smaller than v7.0.0, and not upgraded, use the instructions in this file. -### FreeBSD - -If the installation is on FreeBSD, then set the environment before running any -of the commands below: - -```sh -export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config.ini" -``` - ### SQLite Run From 72232d6cdde970b0aebfa5b977a9b958af57e986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 10:43:23 +0200 Subject: [PATCH 185/424] improve warn handler --- script/zonemaster_backend_rpcapi.psgi | 2 +- script/zonemaster_backend_testagent | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index bae147650..bae6fdf50 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -56,7 +56,7 @@ Log::Any::Adapter->set( ); $SIG{__WARN__} = sub { - $log->warning(map { my $m = $_; $m =~ s/\n/ /g; $m =~ s/^\s+|\s+$//g; $m } @_); + $log->warning(map s/^\s+|\s+$//gr, map s/\n/ /gr, @_); }; my $config = Zonemaster::Backend::Config->load_config(); diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index 58b75b4f6..73a83b235 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -111,7 +111,7 @@ sub log_dispatcher_file { } $SIG{__WARN__} = sub { - $log->warning(map { my $m = $_; $m =~ s/\n/ /g; $m =~ s/^\s+|\s+$//g; $m } @_); + $log->warning(map s/^\s+|\s+$//gr, map s/\n/ /gr, @_); }; ### From b7f76d4ef50f209e4e22fd87e3e8f6ae9a416f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 13:10:08 +0200 Subject: [PATCH 186/424] fix duplicated import --- lib/Zonemaster/Backend/RPCAPI.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index efbc70e0a..203a67925 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -26,7 +26,6 @@ use Zonemaster::Backend; use Zonemaster::Backend::Config; use Zonemaster::Backend::Translator; use Zonemaster::Backend::Validator; -use Log::Any qw( $log ); my $zm_validator = Zonemaster::Backend::Validator->new; my %json_schemas; From 5f551c66cbc2d559185c4de143deea662de06626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 13:50:47 +0200 Subject: [PATCH 187/424] remove unrelated file --- script/zonemaster_db_exporter.psgi | 35 ------------------------------ 1 file changed, 35 deletions(-) delete mode 100644 script/zonemaster_db_exporter.psgi diff --git a/script/zonemaster_db_exporter.psgi b/script/zonemaster_db_exporter.psgi deleted file mode 100644 index bb7144a2d..000000000 --- a/script/zonemaster_db_exporter.psgi +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use Zonemaster::Backend::Config; -use Zonemaster::Backend::DB; -use Net::Prometheus; - -my $client = Net::Prometheus->new; - -my $tests_gauge = $client->new_gauge( - name => 'zonemaster_tests_total', - help => 'Total number of tests', - labels => [ 'state' ], -); - -my $config = Zonemaster::Backend::Config->load_config(); -my $dbtype = $config->DB_engine; -my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); -my $db = $dbclass->from_config( $config ); - -my $prom_app = $client->psgi_app; - -sub { - my $queued = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 0'); - $tests_gauge->labels('queued')->set($queued->{count}); - - my $finished = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 100'); - $tests_gauge->labels('finished')->set($finished->{count}); - - my $running = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress > 0 and progress < 100'); - $tests_gauge->labels('running')->set($running->{count}); - - $prom_app->(@_); -}; From febf070303c40ef04adbd374cca1fb868a3a0aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 17:16:37 +0200 Subject: [PATCH 188/424] improve code --- lib/Zonemaster/Backend/DB/MySQL.pm | 12 ++++++++---- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 12 ++++++++---- lib/Zonemaster/Backend/DB/SQLite.pm | 12 ++++++++---- lib/Zonemaster/Backend/Errors.pm | 8 ++++---- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 473ce396d..0dde54a81 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -229,14 +229,16 @@ sub get_test_params { my ( $params_json ) = $self->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; + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $params_json; my $result; eval { $result = decode_json( $params_json ); }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return decode_json( $params_json ); } @@ -253,7 +255,8 @@ sub test_results { 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}; - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; + 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} ); @@ -265,7 +268,8 @@ sub test_results { } }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index b249b26a8..2c601473b 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -209,11 +209,13 @@ sub get_test_params { 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; + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $params_json; eval { $result = decode_json( encode_utf8( $params_json ) ); }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } @@ -230,7 +232,8 @@ sub test_results { 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; + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $result; eval { # This workaround is needed to properly handle all versions of perl and the DBD::Pg module @@ -254,7 +257,8 @@ sub test_results { } }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id }) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id }) + if $@; return $result; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index ecea51f89..f02eee3dd 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -246,14 +246,16 @@ sub get_test_params { my ( $params_json ) = $self->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; + die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) + unless defined $params_json; my $result; eval { $result = decode_json( $params_json ); }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } @@ -270,7 +272,8 @@ sub test_results { 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}; - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) unless defined $result; + 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} ); @@ -282,7 +285,8 @@ sub test_results { } }; - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) if $@; + die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) + if $@; return $result; } diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index 903579d2c..221d60f2b 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -4,19 +4,19 @@ use Data::Dumper; has 'message' => ( - is => 'rw', + is => 'ro', isa => 'Str', required => 1, ); has 'code' => ( - is => 'rw', + is => 'ro', isa => 'Int', required => 1, ); has 'data' => ( - is => 'rw', + is => 'ro', isa => 'Any', default => undef, ); @@ -70,7 +70,7 @@ has 'reason' => ( ); has 'method' => ( - is => 'rw', + is => 'ro', isa => 'Str', builder => '_build_method' ); From c5378a618b32049aa6d8892ed0de747f39f54350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 17 Aug 2021 16:05:25 +0200 Subject: [PATCH 189/424] add more error types --- lib/Zonemaster/Backend/DB.pm | 3 ++- lib/Zonemaster/Backend/DB/MySQL.pm | 7 ++++--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 +++--- lib/Zonemaster/Backend/DB/SQLite.pm | 7 ++++--- lib/Zonemaster/Backend/Errors.pm | 26 +++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 36f5e5984..a49a95a06 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -62,7 +62,8 @@ sub add_api_user { die "username or api_key not provided to the method add_api_user\n" 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 ); diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 0dde54a81..7151cc790 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -129,7 +129,7 @@ sub user_authorized { sub create_new_batch_job { my ( $self, $username ) = @_; - my ( $batch_id, $creaton_time ) = $self->dbh->selectrow_array( " + my ( $batch_id, $creation_time ) = $self->dbh->selectrow_array( " SELECT batch_id, batch_jobs.creation_time AS batch_creation_time @@ -143,7 +143,8 @@ sub create_new_batch_job { 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 ); + die Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) + if ( $batch_id ); $self->dbh->do( "INSERT INTO batch_jobs (username) VALUES(?)", undef, $username ); my ( $new_batch_id ) = $self->dbh->{mysql_insertid}; @@ -389,7 +390,7 @@ sub add_batch_job { $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; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 2c601473b..61b6d6597 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -154,8 +154,8 @@ sub create_new_batch_job { 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 ); + die Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) + if ( $batch_id ); my ( $new_batch_id ) = $dbh->selectrow_array( "INSERT INTO batch_jobs (username) VALUES (?) RETURNING id", undef, $username ); @@ -358,7 +358,7 @@ sub add_batch_job { $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; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index f02eee3dd..7ee765d70 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -147,7 +147,7 @@ sub user_authorized { sub create_new_batch_job { my ( $self, $username ) = @_; - my ( $batch_id, $creaton_time ) = $self->dbh->selectrow_array( " + my ( $batch_id, $creation_time ) = $self->dbh->selectrow_array( " SELECT batch_id, batch_jobs.creation_time AS batch_creation_time @@ -160,7 +160,8 @@ sub create_new_batch_job { LIMIT 1 " ); - die "You can't create a new batch job, job:[$batch_id] started on:[$creaton_time] still running \n" if ( $batch_id ); + die Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) + 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; @@ -383,7 +384,7 @@ sub add_batch_job { $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; diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index 221d60f2b..54c7f78d9 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -152,6 +152,32 @@ 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; From 2cc1bf8029007da6d7cc924b29892801414501e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 17 Aug 2021 16:29:06 +0200 Subject: [PATCH 190/424] remove error id --- lib/Zonemaster/Backend/Errors.pm | 15 ++++--------- lib/Zonemaster/Backend/RPCAPI.pm | 36 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index 54c7f78d9..fe6deec5d 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -34,9 +34,9 @@ sub as_hash { sub as_string { my $self = shift; - my $str = sprintf "%s (code %d)", $self->message, $self->code; + my $str = sprintf "%s (code %d).", $self->message, $self->code; if (defined $self->data) { - $str .= sprintf "; Context: %s", $self->_data_dump; + $str .= sprintf " Context: %s", $self->_data_dump; } return $str; } @@ -75,12 +75,6 @@ has 'method' => ( builder => '_build_method' ); -has 'id' => ( - is => 'rw', - isa => 'Int', - default => 0, -); - sub _build_method { my $s = 0; while (my @c = caller($s)) { @@ -121,7 +115,6 @@ around 'as_hash' => sub { my $href = $self->$orig; - $href->{exception_id} = $self->id; $href->{reason} = $self->reason; $href->{method} = $self->method; @@ -131,9 +124,9 @@ around 'as_hash' => sub { sub as_string { my $self = shift; - my $str = sprintf "Internal error %0.3d (%s): Unexpected error in the `%s` method: [%s]", $self->id, ref($self), $self->method, $self->reason; + 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; + $str .= sprintf " Context: %s", $self->_data_dump; } return $str; } diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 1be68d685..f4bc3ad41 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -44,7 +44,7 @@ sub new { bless( $self, $type ); if ( ! $params || ! $params->{config} ) { - handle_exception('new', "Missing 'config' parameter", '001'); + handle_exception('new', "Missing 'config' parameter"); } $self->{config} = $params->{config}; @@ -70,16 +70,16 @@ sub _init_db { }; if ($@) { - handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]", '002'); + handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]"); } } sub handle_exception { - my ( $_method, $exception, $exception_id ) = @_; + my ( $_method, $exception ) = @_; if ( !$exception->isa('Zonemaster::Backend::Error') ) { my $reason = $exception; - $exception = Zonemaster::Backend::Error::Internal->new(reason => $reason, id => $exception_id); + $exception = Zonemaster::Backend::Error::Internal->new( reason => $reason ); } if ( $exception->isa('Zonemaster::Backend::Error::Internal') ) { @@ -102,7 +102,7 @@ sub version_info { }; if ($@) { - handle_exception('version_info', $@, '003'); + handle_exception( 'version_info', $@ ); } return \%ver; @@ -115,7 +115,7 @@ sub profile_names { my %profiles; eval { %profiles = $self->{config}->PUBLIC_PROFILES }; if ( $@ ) { - handle_exception( 'profile_names', $@, '004' ); + handle_exception( 'profile_names', $@ ); } return [ keys %profiles ]; @@ -156,7 +156,7 @@ sub get_host_by_name { }; if ($@) { - handle_exception('get_host_by_name', $@, '005'); + handle_exception( 'get_host_by_name', $@ ); } return \@adresses; @@ -206,7 +206,7 @@ sub get_data_from_parent_zone { return \%result; }; if ($@) { - handle_exception('get_data_from_parent_zone', $@, '006'); + handle_exception( 'get_data_from_parent_zone', $@ ); } elsif ($result) { return $result; @@ -300,7 +300,7 @@ $extra_validators{start_domain_test} = sub { return @errors; }; if ($@) { - handle_exception('start_domain_test_validate_syntax', $@, '008'); + handle_exception( 'start_domain_test_validate_syntax', $@ ); } else { return @errors; @@ -340,7 +340,7 @@ sub start_domain_test { $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( 'start_domain_test', $@ ); } return $result; @@ -358,7 +358,7 @@ sub test_progress { $result = $self->{db}->test_progress( $test_id ); }; if ($@) { - handle_exception('test_progress', $@, '010'); + handle_exception( 'test_progress', $@ ); } return $result; @@ -378,7 +378,7 @@ sub get_test_params { $result = $self->{db}->get_test_params( $test_id ); }; if ($@) { - handle_exception('get_test_params', $@, '011'); + handle_exception( 'get_test_params', $@ ); } return $result; @@ -419,7 +419,7 @@ sub get_test_results { my $previous_locale = $translator->locale; if ( !$translator->locale( $locale ) ) { - handle_exception( 'get_test_results', "Failed to set locale: $locale", '017' ); + handle_exception( 'get_test_results', "Failed to set locale: $locale" ); } eval { $translator->data } if $translator; # Provoke lazy loading of translation data @@ -478,7 +478,7 @@ sub get_test_results { $result->{results} = \@zm_results; }; if ($@) { - handle_exception('get_test_results', $@, '012'); + handle_exception( 'get_test_results', $@ ); } $translator->locale( $previous_locale ); @@ -510,7 +510,7 @@ sub get_test_history { $results = $self->{db}->get_test_history( $params ); }; if ($@) { - handle_exception('get_test_history', $@, '013'); + handle_exception( 'get_test_history', $@ ); } return $results; @@ -539,7 +539,7 @@ sub add_api_user { } }; if ($@) { - handle_exception('add_api_user', $@, '014'); + handle_exception( 'add_api_user', $@ ); } return $result; @@ -579,7 +579,7 @@ sub add_batch_job { $results = $self->{db}->add_batch_job( $params ); }; if ($@) { - handle_exception('add_batch_job', $@, '015'); + handle_exception( 'add_batch_job', $@ ); } return $results; @@ -599,7 +599,7 @@ sub get_batch_job_result { $result = $self->{db}->get_batch_job_result($batch_id); }; if ($@) { - handle_exception('get_batch_job_result', $@, '016'); + handle_exception( 'get_batch_job_result', $@ ); } return $result; From b7716d53e3aacec0bdb113a58d8de6bd41660d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 17 Aug 2021 16:29:47 +0200 Subject: [PATCH 191/424] use error classes instead of strings --- lib/Zonemaster/Backend/DB.pm | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index a49a95a06..aaadd70b3 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -51,7 +51,8 @@ sub get_db_class { 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 ); } @@ -59,15 +60,16 @@ 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 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; } From 06ff79f4d1dd6edb5894cc91326c3f45c74cc5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 18 Aug 2021 11:58:14 +0200 Subject: [PATCH 192/424] remove unused import --- lib/Zonemaster/Backend/DB.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 6cd39598d..91ec27160 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -10,7 +10,6 @@ use JSON::PP; use Digest::MD5 qw(md5_hex); use Encode; use Log::Any qw( $log ); -use Time::HiRes qw[time]; use Zonemaster::Engine::Profile From e0afd77ffe95c84730355ffbad9197eff1d6d9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 18 Aug 2021 13:27:17 +0200 Subject: [PATCH 193/424] change make undelegated a boolean --- docs/API.md | 4 ++-- lib/Zonemaster/Backend/RPCAPI.pm | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 04fc56351..287be8aa5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -944,12 +944,12 @@ Example response: { "id": "c45a3f8256c4a155", "creation_time": "2016-11-15 11:53:13.965982", - "undelegated": 1, + "undelegated": true, "overall_result": "error", }, { "id": "32dd4bc0582b6bf9", - "undelegated": 0, + "undelegated": false, "creation_time": "2016-11-14 08:46:41.532047", "overall_result": "error", }, diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 6ea7273bb..abb3b43e2 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -559,6 +559,9 @@ 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'); From 486b708bcf05fe50e036f7370df7e6f1665a5d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 18 Aug 2021 16:09:48 +0200 Subject: [PATCH 194/424] add custom error message in json schema --- lib/Zonemaster/Backend/RPCAPI.pm | 200 +++++++++++++++++++--------- lib/Zonemaster/Backend/Validator.pm | 66 +++++++-- 2 files changed, 192 insertions(+), 74 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index fae0d9b31..c151ac292 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -13,6 +13,9 @@ 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; # Zonemaster Modules use Zonemaster::LDNS; @@ -131,9 +134,14 @@ sub get_language_tags { 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; @@ -162,9 +170,14 @@ $extra_validators{get_data_from_parent_zone} = sub { } return @errors; }; -$json_schemas{get_data_from_parent_zone} = joi->object->strict->props( - domain => $zm_validator->domain_name->required -); +$json_schemas{get_data_from_parent_zone} = { + type => 'object', + additionalProperties => 0, + required => [ 'domain' ], + properties => { + domain => $zm_validator->domain_name + } +}; sub get_data_from_parent_zone { my ( $self, $params ) = @_; @@ -297,24 +310,31 @@ $extra_validators{start_domain_test} = sub { } }; -$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( - # NOTE: Array items are not compiled automatically, so to enforce all properties we need to do it by hand - $zm_validator->nameserver->compile - ), - ds_info => joi->array->items( - $zm_validator->ds_info->compile - ), - 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{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, + + } +}; sub start_domain_test { my ( $self, $params ) = @_; @@ -374,10 +394,15 @@ sub get_test_params { 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 ) = @_; @@ -479,14 +504,23 @@ sub get_test_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, + 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 ) = @_; @@ -535,29 +569,36 @@ sub add_api_user { 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 ) = @_; @@ -652,9 +693,46 @@ sub validate_params { my @error_response = (); - my @json_validation_error = $method_schema->validate( $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 ) { + print Data::Dumper::Dumper($err); + + my $message = $err->message; + my @details = @{$err->details}; + + # Handle 'required' errors globally so it does not get overwritten + if (@details[1] eq 'required') { + $message = '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); + } - push @error_response, map { { message => $_->message, path => $_->path } } @json_validation_error; + 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 ( $extra_validators{$method} ) { diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 6f7b6541f..907252e1d 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -122,24 +122,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' => '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', + regex => $DIGEST_RE, + 'x-error-message' => 'Invalid digest format' + }, + algorithm => { + type => 'number', + minimum => 0, + 'x-error-message' => 'Algorithm must be a positive integer' + }, + digtype => { + type => 'number', + minimum => 0, + 'x-error-message' => 'Digest type must be a positive integer' + }, + keytag => { + type => 'number', + minimum => 0, + 'x-error-message' => 'Keytag must be a positive integer' + } + } + }; } sub ip_address { - return joi->string->regex( $IPADDR_RE ); + return { + type => 'string', + pattern => $IPADDR_RE, + 'x-error-message' => '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; @@ -154,7 +190,11 @@ sub test_id { return joi->string->regex( $TEST_ID_RE ); } sub language_tag { - return joi->string->regex( $LANGUAGE_RE ); + return { + type => 'string', + regex => $LANGUAGE_RE, + 'x-error-message' => 'Invalid language tag format' + }; } sub username { return joi->string->regex( $USERNAME_RE ); From 53bada292846de55e93b1364d407778ddbbb49ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 18 Aug 2021 16:23:17 +0200 Subject: [PATCH 195/424] fix patterns --- lib/Zonemaster/Backend/Validator.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 907252e1d..f421b6d26 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -136,7 +136,7 @@ sub ds_info { properties => { digest => { type => 'string', - regex => $DIGEST_RE, + pattern => $DIGEST_RE, 'x-error-message' => 'Invalid digest format' }, algorithm => { @@ -192,7 +192,7 @@ sub test_id { sub language_tag { return { type => 'string', - regex => $LANGUAGE_RE, + pattern => $LANGUAGE_RE, 'x-error-message' => 'Invalid language tag format' }; } From a519a7aadfe838a8fecfcc909cca07c9917c2b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 19 Aug 2021 10:19:23 +0200 Subject: [PATCH 196/424] Make zm-rpcapi not reload its config for each worker on BSD --- share/zm_rpcapi-bsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/zm_rpcapi-bsd b/share/zm_rpcapi-bsd index 329262051..0e92e4e74 100755 --- a/share/zm_rpcapi-bsd +++ b/share/zm_rpcapi-bsd @@ -21,7 +21,7 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. #export ZM_BACKEND_RPCAPI_LOGLEVEL='warning' # Set this variable to override the default log level command="/usr/local/bin/starman" -command_args="--daemonize --user=${zm_rpcapi_user} --group=${zm_rpcapi_group} --pid=${zm_rpcapi_pidfile} --error-log=${zm_rpcapi_logfile} --listen=${zm_rpcapi_listen} --app /usr/local/bin/zonemaster_backend_rpcapi.psgi" +command_args="--daemonize --user=${zm_rpcapi_user} --group=${zm_rpcapi_group} --pid=${zm_rpcapi_pidfile} --error-log=${zm_rpcapi_logfile} --listen=${zm_rpcapi_listen} --preload-app /usr/local/bin/zonemaster_backend_rpcapi.psgi" pidfile="${zm_rpcapi_pidfile}" required_files="/usr/local/etc/zonemaster/backend_config.ini /usr/local/bin/zonemaster_backend_rpcapi.psgi" From b4d75375263b538247caa2d875387da41e458578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Thu, 19 Aug 2021 10:28:48 +0200 Subject: [PATCH 197/424] Reorder starman options for easier comparison Also, put Plack::Runner arguments last. --- share/zm-rpcapi.lsb | 2 +- share/zm_rpcapi-bsd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/zm-rpcapi.lsb b/share/zm-rpcapi.lsb index 862d50510..d0f3ca27c 100644 --- a/share/zm-rpcapi.lsb +++ b/share/zm-rpcapi.lsb @@ -27,7 +27,7 @@ STARMAN=`PATH="$PATH:/usr/local/bin" /usr/bin/which starman` . /lib/lsb/init-functions start () { - $STARMAN --user=$USER --group=$GROUP --error-log=$LOGFILE --pid=$PIDFILE --listen=$LISTENIP:$LISTENPORT --preload-app --daemonize $BINDIR/zonemaster_backend_rpcapi.psgi || exit 1 + $STARMAN --listen=$LISTENIP:$LISTENPORT --preload-app --user=$USER --group=$GROUP --pid=$PIDFILE --error-log=$LOGFILE --daemonize $BINDIR/zonemaster_backend_rpcapi.psgi || exit 1 } stop () { diff --git a/share/zm_rpcapi-bsd b/share/zm_rpcapi-bsd index 0e92e4e74..193bd471a 100755 --- a/share/zm_rpcapi-bsd +++ b/share/zm_rpcapi-bsd @@ -21,7 +21,7 @@ export ZONEMASTER_BACKEND_CONFIG_FILE="/usr/local/etc/zonemaster/backend_config. #export ZM_BACKEND_RPCAPI_LOGLEVEL='warning' # Set this variable to override the default log level command="/usr/local/bin/starman" -command_args="--daemonize --user=${zm_rpcapi_user} --group=${zm_rpcapi_group} --pid=${zm_rpcapi_pidfile} --error-log=${zm_rpcapi_logfile} --listen=${zm_rpcapi_listen} --preload-app /usr/local/bin/zonemaster_backend_rpcapi.psgi" +command_args="--listen=${zm_rpcapi_listen} --preload-app --user=${zm_rpcapi_user} --group=${zm_rpcapi_group} --pid=${zm_rpcapi_pidfile} --error-log=${zm_rpcapi_logfile} --daemonize /usr/local/bin/zonemaster_backend_rpcapi.psgi" pidfile="${zm_rpcapi_pidfile}" required_files="/usr/local/etc/zonemaster/backend_config.ini /usr/local/bin/zonemaster_backend_rpcapi.psgi" From 5f9ee3ef68dc3da61356c7ae057bc94463651751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 23 Aug 2021 09:52:17 +0200 Subject: [PATCH 198/424] remove specialized as_hash method for internal error --- lib/Zonemaster/Backend/Errors.pm | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index fe6deec5d..9d310e99d 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -109,19 +109,6 @@ around 'reason' => sub { $self->$orig($value); }; -around 'as_hash' => sub { - my $orig = shift; - my $self = shift; - - my $href = $self->$orig; - - $href->{reason} = $self->reason; - $href->{method} = $self->method; - - return $href; -}; - - sub as_string { my $self = shift; my $str = sprintf "Caught %s in the `%s` method: %s", ref($self), $self->method, $self->reason; From 9d22befef93e939889549aa0d717a96a19d0af5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 23 Aug 2021 09:56:25 +0200 Subject: [PATCH 199/424] remove unused variable --- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index bc2b6669b..b4c936979 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -75,7 +75,7 @@ sub _init_db { } sub handle_exception { - my ( $_method, $exception ) = @_; + my ( undef, $exception ) = @_; if ( !$exception->isa('Zonemaster::Backend::Error') ) { my $reason = $exception; From 9dc0b3dc433c94f387e265609843116fd0b319b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 23 Aug 2021 09:59:53 +0200 Subject: [PATCH 200/424] improve code formatting --- lib/Zonemaster/Backend/DB.pm | 8 ++++---- lib/Zonemaster/Backend/DB/MySQL.pm | 8 ++++---- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 8 ++++---- lib/Zonemaster/Backend/DB/SQLite.pm | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index aaadd70b3..09875a82c 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -52,7 +52,7 @@ sub user_exists { my ( $self, $user ) = @_; die Zonemaster::Backend::Error::Internal->new( reason => "username not provided to the method user_exists") - unless ( $user ); + unless ( $user ); return $self->user_exists_in_db( $user ); } @@ -61,15 +61,15 @@ sub add_api_user { my ( $self, $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 ); + unless ( $username && $api_key ); die Zonemaster::Backend::Error::Conflict->new( message => 'User already exists', data => { username => $username } ) - if ( $self->user_exists( $username ) ); + if ( $self->user_exists( $username ) ); my $result = $self->add_api_user_to_db( $username, $api_key ); die Zonemaster::Backend::Error::Internal->new( reason => "add_api_user_to_db not successful") - unless ( $result ); + unless ( $result ); return $result; } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 7151cc790..932fcbd53 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -231,7 +231,7 @@ sub get_test_params { my ( $params_json ) = $self->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; + unless defined $params_json; my $result; eval { @@ -239,7 +239,7 @@ sub get_test_params { }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; + if $@; return decode_json( $params_json ); } @@ -257,7 +257,7 @@ sub test_results { $result = $hrefs->{$test_id}; die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $result; + unless defined $result; eval { $result->{params} = decode_json( $result->{params} ); @@ -270,7 +270,7 @@ sub test_results { }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; + if $@; return $result; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 61b6d6597..bcd5dfcdd 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -210,12 +210,12 @@ sub get_test_params { 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; + unless defined $params_json; eval { $result = decode_json( encode_utf8( $params_json ) ); }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; + if $@; return $result; } @@ -233,7 +233,7 @@ sub test_results { $result = $hrefs->{$test_id}; die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $result; + unless defined $result; eval { # This workaround is needed to properly handle all versions of perl and the DBD::Pg module @@ -258,7 +258,7 @@ sub test_results { }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id }) - if $@; + if $@; return $result; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 7ee765d70..fb0603122 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -248,7 +248,7 @@ sub get_test_params { my ( $params_json ) = $self->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; + unless defined $params_json; my $result; eval { @@ -256,7 +256,7 @@ sub get_test_params { }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; + if $@; return $result; } @@ -274,7 +274,7 @@ sub test_results { $result = $hrefs->{$test_id}; die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $result; + unless defined $result; eval { $result->{params} = decode_json( $result->{params} ); @@ -287,7 +287,7 @@ sub test_results { }; die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; + if $@; return $result; } From 5a04135d56f87d654dedd9dab004001a1d735a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 23 Aug 2021 10:19:44 +0200 Subject: [PATCH 201/424] change method name --- lib/Zonemaster/Backend/DB.pm | 3 ++- lib/Zonemaster/Backend/DB/MySQL.pm | 2 +- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 +- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 91ec27160..cf07c9c75 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -27,6 +27,7 @@ requires qw( test_results user_authorized user_exists_in_db + get_relative_start_time ); =head2 get_db_class @@ -167,7 +168,7 @@ sub process_dead_test { if ( $nb_retries < $test_run_max_retries) { $self->schedule_for_retry($hash_id); } else { - $self->force_end_test($hash_id, $results, $self->get_timestamp($hash_id)); + $self->force_end_test($hash_id, $results, $self->get_relative_start_time($hash_id)); } } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index af7b590b5..43257ab6e 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -423,7 +423,7 @@ sub schedule_for_retry { $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); } -sub get_timestamp { +sub get_relative_start_time { my ( $self, $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 41ee60b04..da60700f1 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -395,7 +395,7 @@ sub schedule_for_retry { $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NOW() WHERE hash_id=?", undef, $hash_id); } -sub get_timestamp { +sub get_relative_start_time { my ( $self, $hash_id ) = @_; return $self->dbh->selectrow_array("SELECT EXTRACT(EPOCH FROM now() - test_start_time) FROM test_results WHERE hash_id=?", undef, $hash_id); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 1be1bedf7..a5986ef2c 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -416,7 +416,7 @@ sub schedule_for_retry { $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); } -sub get_timestamp { +sub get_relative_start_time { my ( $self, $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); From 25625ffc5c8fa88ee089929f7852170447ede7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 24 Aug 2021 13:20:29 +0200 Subject: [PATCH 202/424] improve code readability --- share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 4 ++-- share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index 59615b82b..2f347d67e 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -21,8 +21,8 @@ sub patch_db { while ( my $row = $sth1->fetchrow_hashref ) { my $id = $row->{id}; my $raw_params = decode_json($row->{params}); - my $ds_info_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); - my $nameservers_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{nameservers}}); + 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); diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 828d5b481..a8311fc08 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -21,8 +21,8 @@ sub patch_db { while ( my $row = $sth1->fetchrow_hashref ) { my $id = $row->{id}; my $raw_params = decode_json($row->{params}); - my $ds_info_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{ds_info}}); - my $nameservers_values = scalar( map { grep (!/^$/, values( %$_ ) ) } @{$raw_params->{nameservers}}); + 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); From 169019f88c5abe92b805c9751ccedb18752e0d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 15:39:08 +0200 Subject: [PATCH 203/424] improve code formatting --- lib/Zonemaster/Backend/DB/MySQL.pm | 2 +- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 +- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 932fcbd53..d95070993 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -144,7 +144,7 @@ sub create_new_batch_job { ", 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 ); + if ( $batch_id ); $self->dbh->do( "INSERT INTO batch_jobs (username) VALUES(?)", undef, $username ); my ( $new_batch_id ) = $self->dbh->{mysql_insertid}; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index bcd5dfcdd..1177066a7 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -155,7 +155,7 @@ sub create_new_batch_job { 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 ); + if ( $batch_id ); my ( $new_batch_id ) = $dbh->selectrow_array( "INSERT INTO batch_jobs (username) VALUES (?) RETURNING id", undef, $username ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index fb0603122..72919151e 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -161,7 +161,7 @@ sub create_new_batch_job { " ); die Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) - if ( $batch_id ); + 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; From b32c1b99f22120de6a4464f3564bfd0ae1c8b9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 15:49:49 +0200 Subject: [PATCH 204/424] make reason read only --- lib/Zonemaster/Backend/Errors.pm | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/lib/Zonemaster/Backend/Errors.pm b/lib/Zonemaster/Backend/Errors.pm index 9d310e99d..cc25cf877 100644 --- a/lib/Zonemaster/Backend/Errors.pm +++ b/lib/Zonemaster/Backend/Errors.pm @@ -65,8 +65,7 @@ has '+code' => ( has 'reason' => ( isa => 'Str', - is => 'rw', - initializer => 'reason', + is => 'ro' ); has 'method' => ( @@ -89,24 +88,16 @@ sub _build_method { return $c[3]; } -around 'reason' => sub { - my $orig = shift; - my $self = shift; - - my ( $value, $setter, $attr ) = @_; - - # reader - return $self->$orig if not $value; +around 'BUILDARGS', sub { + my ($orig, $class, %args) = @_; - # trim new lines - $value =~ s/\n/ /g; - $value =~ s/^\s+|\s+$//g; - - # initializer - return $setter->($value) if $setter; + if(exists $args{reason}) { + # trim new lines + $args{reason} =~ s/\n/ /g; + $args{reason} =~ s/^\s+|\s+$//g; + } - # writer - $self->$orig($value); + $class->$orig(%args); }; sub as_string { From 1c803dfa9ce6f269d0941c154409d217290de9c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 26 Aug 2021 10:03:10 +0200 Subject: [PATCH 205/424] remove method name from handle_exception calls --- lib/Zonemaster/Backend/RPCAPI.pm | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index b4c936979..b36a05813 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -44,7 +44,7 @@ sub new { bless( $self, $type ); if ( ! $params || ! $params->{config} ) { - handle_exception('new', "Missing 'config' parameter"); + handle_exception("Missing 'config' parameter"); } $self->{config} = $params->{config}; @@ -70,12 +70,12 @@ sub _init_db { }; if ($@) { - handle_exception('_init_db', "Failed to initialize the [$dbtype] database backend module: [$@]"); + handle_exception("Failed to initialize the [$dbtype] database backend module: [$@]"); } } sub handle_exception { - my ( undef, $exception ) = @_; + my ( $exception ) = @_; if ( !$exception->isa('Zonemaster::Backend::Error') ) { my $reason = $exception; @@ -102,7 +102,7 @@ sub version_info { }; if ($@) { - handle_exception( 'version_info', $@ ); + handle_exception( $@ ); } return \%ver; @@ -115,7 +115,7 @@ sub profile_names { my %profiles; eval { %profiles = $self->{config}->PUBLIC_PROFILES }; if ( $@ ) { - handle_exception( 'profile_names', $@ ); + handle_exception( $@ ); } return [ keys %profiles ]; @@ -156,7 +156,7 @@ sub get_host_by_name { }; if ($@) { - handle_exception( 'get_host_by_name', $@ ); + handle_exception( $@ ); } return \@adresses; @@ -206,7 +206,7 @@ sub get_data_from_parent_zone { return \%result; }; if ($@) { - handle_exception( 'get_data_from_parent_zone', $@ ); + handle_exception( $@ ); } elsif ($result) { return $result; @@ -300,7 +300,7 @@ $extra_validators{start_domain_test} = sub { return @errors; }; if ($@) { - handle_exception( 'start_domain_test_validate_syntax', $@ ); + handle_exception( $@ ); } else { return @errors; @@ -340,7 +340,7 @@ sub start_domain_test { $result = $self->{db}->create_new_test( $params->{domain}, $params, $self->{config}->ZONEMASTER_age_reuse_previous_test ); }; if ($@) { - handle_exception( 'start_domain_test', $@ ); + handle_exception( $@ ); } return $result; @@ -358,7 +358,7 @@ sub test_progress { $result = $self->{db}->test_progress( $test_id ); }; if ($@) { - handle_exception( 'test_progress', $@ ); + handle_exception( $@ ); } return $result; @@ -378,7 +378,7 @@ sub get_test_params { $result = $self->{db}->get_test_params( $test_id ); }; if ($@) { - handle_exception( 'get_test_params', $@ ); + handle_exception( $@ ); } return $result; @@ -419,7 +419,7 @@ sub get_test_results { my $previous_locale = $translator->locale; if ( !$translator->locale( $locale ) ) { - handle_exception( 'get_test_results', "Failed to set locale: $locale" ); + handle_exception( "Failed to set locale: $locale" ); } eval { $translator->data } if $translator; # Provoke lazy loading of translation data @@ -478,7 +478,7 @@ sub get_test_results { $result->{results} = \@zm_results; }; if ($@) { - handle_exception( 'get_test_results', $@ ); + handle_exception( $@ ); } $translator->locale( $previous_locale ); @@ -510,7 +510,7 @@ sub get_test_history { $results = $self->{db}->get_test_history( $params ); }; if ($@) { - handle_exception( 'get_test_history', $@ ); + handle_exception( $@ ); } return $results; @@ -539,7 +539,7 @@ sub add_api_user { } }; if ($@) { - handle_exception( 'add_api_user', $@ ); + handle_exception( $@ ); } return $result; @@ -579,7 +579,7 @@ sub add_batch_job { $results = $self->{db}->add_batch_job( $params ); }; if ($@) { - handle_exception( 'add_batch_job', $@ ); + handle_exception( $@ ); } return $results; @@ -599,7 +599,7 @@ sub get_batch_job_result { $result = $self->{db}->get_batch_job_result($batch_id); }; if ($@) { - handle_exception( 'get_batch_job_result', $@ ); + handle_exception( $@ ); } return $result; From c50e8b46ace5c19288115fde945fd52451cb002c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 31 Aug 2021 15:15:26 +0200 Subject: [PATCH 206/424] update manifest --- MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST b/MANIFEST index 28b38eb1a..a62312754 100644 --- a/MANIFEST +++ b/MANIFEST @@ -29,6 +29,7 @@ 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/RPCAPI.pm lib/Zonemaster/Backend/TestAgent.pm lib/Zonemaster/Backend/Translator.pm From 0dc62fe8ab82a79010b77d7c953d6d2d7d9ba0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 1 Sep 2021 17:30:05 +0200 Subject: [PATCH 207/424] change postgres implementation --- ...tgresql_db_zonemaster_backend_ver_7.0.0.pl | 80 +++++-------------- 1 file changed, 21 insertions(+), 59 deletions(-) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 67571864c..147e1394f 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -1,5 +1,7 @@ use strict; use warnings; +use JSON::PP; +use Encode; use DBI qw(:utils); @@ -10,69 +12,29 @@ if ( $config->DB_engine ne 'PostgreSQL' ) { die "The configuration file does not contain the PostgreSQL backend"; } -my $dbh = Zonemaster::Backend::DB::PostgreSQL->from_config( $config )->dbh; +my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); +my $dbh = $db->dbh; + sub patch_db { + 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; - #################################################################### - # TEST RESULTS - #################################################################### - eval { - $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); - }; - if ($@) { - print "Error while changing DB schema: " . $@; - } + if (utf8::is_utf8($row->{params}) ) { + $raw_params = decode_json( encode_utf8 ( $row->{params} ) ); + } else { + $raw_params = decode_json( $row->{params} ); + } - $dbh->do( qq[ -update test_results set undelegated = test_results_undelegated.undelegated_bool::int -from ( - select - test_results.id, - ( - case when ds_filled.ds_filled is null then false else ds_filled.ds_filled end - or - case when ns_filled.ns_filled is null then false else ns_filled.ns_filled end - ) as undelegated_bool - from test_results - left join ( - select - count(*) > 0 as ds_filled, - id - from ( - select - jd.value, - id - from - ( - select json_array_elements(params->'ds_info') as ja, id - from test_results - ) as s1, - json_each_text(ja) as jd - ) as s2 - where value is not null and value::text != ''::text - group by id - ) as ds_filled on ds_filled.id = test_results.id - left join ( - select - count(*) > 0 as ns_filled, - id - from ( - select - jd.value, - id - from - ( - select json_array_elements(params->'nameservers') as ja, id - from test_results - ) as s1, - json_each_text(ja) as jd - ) as s2 - where value is not null and value::text != ''::text - group by id - ) as ns_filled on ns_filled.id = test_results.id -) as test_results_undelegated where test_results.id = test_results_undelegated.id; - ] ); + 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); + } } patch_db(); From d86fe181096a021eaaafc65675b0645cefb4256f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 2 Sep 2021 09:41:35 +0200 Subject: [PATCH 208/424] fix tests --- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- t/validator.t | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index c151ac292..a6033debe 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -706,7 +706,7 @@ sub validate_params { my @details = @{$err->details}; # Handle 'required' errors globally so it does not get overwritten - if (@details[1] eq 'required') { + if ($details[1] eq 'required') { $message = 'Missing property'; } else { my @path = split '/', $err->path, -1; diff --git a/t/validator.t b/t/validator.t index 0e8ad36ec..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,7 +37,7 @@ 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'; }; From c18417ce85a0d61e022dcafb8e311b63f569b686 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 2 Sep 2021 11:56:39 +0200 Subject: [PATCH 209/424] Keep the "queue" parameter to compute fingerprint The "queue" parameter has been kept because it is linked to a TestAgent which can live in a specific environment. Using a pool of several TestAgents in different testing environments each one with its own queue, and adding the same test to each queue should result in the creation of several tests (one per TestAgent in the pool). Then each agent can run its own tests and produce its own results. The outcomes might differ from on TestAgent to another. Since the fingerprint is calculated with parameters that may affect the outcome of a test, then the "queue" should be used in this computation. The "priority" parameter however does not impact the outcome of a test. --- docs/API.md | 4 ++-- lib/Zonemaster/Backend/DB.pm | 1 + t/db.t | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/API.md b/docs/API.md index 377dfea64..dcb828ce5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -691,8 +691,8 @@ the `test id` of the previous test will be returned. The default value of 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`. +considered to be the same are `domain`, `ipv6`, `ipv4`, `nameservers`, `ds_info`, +`profile` and `queue`. #### `"error"` diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 3020a7978..68a16954c 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -196,6 +196,7 @@ sub _normalize_params { $normalized{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); $normalized{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); $normalized{profile} = $$params{profile} // "default"; + $normalized{queue} = $$params{queue} // 0; my $array_ds_info = $$params{ds_info} // []; my @array_ds_info_sort = sort { diff --git a/t/db.t b/t/db.t index b78b9a9c5..ca6da0752 100644 --- a/t/db.t +++ b/t/db.t @@ -19,7 +19,7 @@ sub encode_and_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 $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default","queue":0}'; my %params = ( domain => "example.com" ); @@ -120,7 +120,7 @@ subtest 'encoding and fingerprint' => sub { }; 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 $expected_encoded_params = '{"client":"GUI v3.3.0","domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default","queue":0}'; my %params1 = ( domain => "example.com", ); From 12b8bff6c52f0497e817097319c0f3b53d51f2f8 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 2 Sep 2021 12:01:20 +0200 Subject: [PATCH 210/424] Test that fingerprints are different --- t/db.t | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/t/db.t b/t/db.t index ca6da0752..5de3c332d 100644 --- a/t/db.t +++ b/t/db.t @@ -134,6 +134,37 @@ subtest 'encoding and fingerprint' => sub { 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 queues' => sub { + my %params1 = ( + domain => "example.com", + queue => 1 + ); + my %params2 = ( + domain => "example.com", + queue => 2 + ); + my ( undef, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); + my ( undef, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); + + isnt $fingerprint1, $fingerprint2, 'different queues, different fingerprints'; + }; + } }; done_testing(); From 85f7e8d6cfebe93dba3cb7db0968c1d001c39334 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 30 Aug 2021 14:49:19 +0200 Subject: [PATCH 211/424] Refactor fingerprint computation The fingerpring computation uses a _projection_ not a _normalization_ of some of the parameters. The code is updated to better reflect that operation. --- lib/Zonemaster/Backend/DB.pm | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 68a16954c..e1069ff15 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -183,20 +183,20 @@ sub _new_dbh { return $dbh; } -sub _normalize_params { +sub _project_params { my ( $self, $params ) = @_; my $profile = Zonemaster::Engine::Profile->effective; - my %normalized = (); + my %projection = (); # some of these values are already set in RPCAPI # however setting them here again is required for testing purpose - $normalized{domain} = lc $$params{domain} // ""; - $normalized{ipv4} = $$params{ipv4} // $profile->get( 'net.ipv4' ); - $normalized{ipv6} = $$params{ipv6} // $profile->get( 'net.ipv6' ); - $normalized{profile} = $$params{profile} // "default"; - $normalized{queue} = $$params{queue} // 0; + $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"; + $projection{queue} = $$params{queue} // 0; my $array_ds_info = $$params{ds_info} // []; my @array_ds_info_sort = sort { @@ -206,7 +206,7 @@ sub _normalize_params { $a->{keytag} <=> $b->{keytag} } @$array_ds_info; - $normalized{ds_info} = \@array_ds_info_sort; + $projection{ds_info} = \@array_ds_info_sort; my $array_nameservers = $$params{nameservers} // []; for my $nameserver (@$array_nameservers) { @@ -220,9 +220,9 @@ sub _normalize_params { ( defined $a->{ip} and defined $b->{ip} and $a->{ip} cmp $b->{ip} ) } @$array_nameservers; - $normalized{nameservers} = \@array_nameservers_sort; + $projection{nameservers} = \@array_nameservers_sort; - return \%normalized; + return \%projection; } sub _params_to_json_str { @@ -238,9 +238,10 @@ sub _params_to_json_str { =head2 encode_params -Encode the params object into a JSON string. The object is first normalized and -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 +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 @@ -248,8 +249,8 @@ L_normalize_params( $params ); - $params = { %$params, %$normalized_params }; + my $projected_params = $self->_project_params( $params ); + $params = { %$params, %$projected_params }; my $encoded_params = $self->_params_to_json_str( $params ); return $encoded_params; @@ -258,7 +259,7 @@ sub encode_params { =head2 generate_fingerprint Returns a fingerprint of the hash passed in argument. -The fingerprint is computed after normalizing the hash. +The fingerprint is computed after projecting the hash. Such fingerprint are usefull to find similar tests in the database. =cut @@ -266,8 +267,8 @@ Such fingerprint are usefull to find similar tests in the database. sub generate_fingerprint { my ( $self, $params ) = @_; - my $normalized_params = $self->_normalize_params( $params ); - my $encoded_params = $self->_params_to_json_str( $normalized_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; @@ -278,22 +279,21 @@ sub generate_fingerprint { 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 normalization. +"nameservers" parameters is are defined with data after projection. =cut sub undelegated { my ( $self, $params ) = @_; - my $normalized_params = $self->_normalize_params( $params ); + my $projected_params = $self->_project_params( $params ); - return 1 if defined( $$normalized_params{ds_info}[0] ); - return 1 if defined( $$normalized_params{nameservers}[0] ); + return 1 if defined( $$projected_params{ds_info}[0] ); + return 1 if defined( $$projected_params{nameservers}[0] ); return 0; } - no Moose::Role; 1; From 5214042bb45c00fd4a000a521d78e55b923b4a21 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 2 Sep 2021 15:46:07 +0200 Subject: [PATCH 212/424] Merge Ubuntu section into Debian's --- docs/Installation.md | 58 ++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index a2747ef58..b7b52da13 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -9,22 +9,21 @@ * [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) +* [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-debian-and-ubuntu) + * [4.2 Database engine installation and configuration (Debian/Ubuntu)](#42-database-engine-installation-and-configuration-debian-and-ubuntu) + * [4.3 Service configuration and startup (Debian/Ubuntu)](#43-service-configuration-and-startup-debian-and-ubuntu) + * [4.4 Post-installation (Debian/Ubuntu)](#44-post-installation-debian-and-ubuntu) * [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) +* [6. Post-installation](#6-post-installation) + * [6.1 Smoke test](#61-smoke-test) + * [6.2 What to do next?](#62-what-to-do-next) + * [6.3 Cleaning up the database](#63-cleaning-up-the-database) + * [6.4 Upgrade Zonemaster database](#64-upgrade-zonemaster-database) ## 1. Overview @@ -241,9 +240,9 @@ sudo systemctl start zm-testagent 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 and Ubuntu) > **Note:** Zonemaster::LDNS and Zonemaster::Engine are not listed here as they > are dealt with in the [prerequisites](#prerequisites) section. @@ -311,7 +310,7 @@ 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 and configuration (Debian and Ubuntu) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. @@ -326,7 +325,7 @@ 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) +#### 4.2.1 Instructions for MariaDB (Debian and Ubuntu) Install the database engine and its dependencies: @@ -353,7 +352,7 @@ sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backe > file). This user has just enough permissions to run the backend software. -#### 4.2.2 Instructions for PostgreSQL (Debian) +#### 4.2.2 Instructions for PostgreSQL (Debian and Ubuntu) Install database engine and Perl bindings: @@ -380,7 +379,7 @@ sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zone > This user has just enough permissions to run the backend software. -#### 4.2.3 Instructions for SQLite (Debian) +#### 4.2.3 Instructions for SQLite (Debian and Ubuntu) > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. @@ -408,7 +407,7 @@ sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite > **Note:** See the [backend configuration] documentation for details. -### 4.3 Service configuration and startup (Debian) +### 4.3 Service configuration and startup (Debian and Ubuntu) Make sure our tmpfiles configuration takes effect: @@ -426,7 +425,7 @@ sudo systemctl start zm-testagent ``` -### 4.4 Post-installation (Debian) +### 4.4 Post-installation (Debian and Ubuntu) See the [post-installation] section for post-installation matters. @@ -636,14 +635,9 @@ service zm_testagent status See the [post-installation] section for post-installation matters. -## 6. Installation on Ubuntu +## 6. Post-installation -Use the procedure for installation on [Debian](#4-installation-on-debian). - - -## 7. Post-installation - -### 7.1 Smoke test +### 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. @@ -657,14 +651,14 @@ followed by a percentage ticking up from 0% to 100%. Once the number reaches 100% a JSON object is printed and zmtest terminates. -### 7.2. What to do next? +### 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.3. Cleaning up the database +### 6.3. 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` @@ -672,25 +666,25 @@ 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 +#### 6.3.1 MySQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` mysql --user=root --password < ./cleanup-mysql.sql ``` -#### 7.3.2 PostgreSQL +#### 6.3.2 PostgreSQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! ``` -#### 7.3.3 SQLite +#### 6.3.3 SQLite Remove the database file and recreate it following the installation instructions above. -### 7.4 Upgrade Zonemaster database +### 6.4 Upgrade Zonemaster database If you upgrade your Zonemaster installation with a newer version of Zonemaster-Backend and keep the database, then you might have to upgrade the From bd0d3a84fabbaa649a35064df4bf7e4581f5872b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 2 Sep 2021 17:06:49 +0200 Subject: [PATCH 213/424] Document installation using SQLite by default Other database engines (MariaDB/MySQL and PostgreSQL) installation instructions are moved to appendices. --- docs/Installation.md | 491 ++++++++++++++++++++++++------------------- 1 file changed, 269 insertions(+), 222 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index b7b52da13..c101c80ca 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -22,8 +22,11 @@ * [6. Post-installation](#6-post-installation) * [6.1 Smoke test](#61-smoke-test) * [6.2 What to do next?](#62-what-to-do-next) - * [6.3 Cleaning up the database](#63-cleaning-up-the-database) - * [6.4 Upgrade Zonemaster database](#64-upgrade-zonemaster-database) +* [7. Upgrade Zonemaster database](#7-upgrade-zonemaster-database) +* [Appendices](#appendices) + * [A. Installation with MariaDB](#a-installation-with-mariadb) + * [B. Installation with PostgreSQL](#b-installation-with-postgresql) + * [C. Cleaning up the database](#c-cleaning-up-the-database) ## 1. Overview @@ -110,90 +113,15 @@ 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 upgrade and want to keep the database, go to [section 7][Upgrade +database] first. If you instead want to start from afresh, then go to [appendix +C][Cleaning 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 - ``` - -* 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 - ``` - -Initialize Zonemaster database (unless you keep an old database): - -```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.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.3 Instructions for SQLite (CentOS) +#### 3.2.1 Instructions for SQLite (CentOS) > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. @@ -217,6 +145,12 @@ sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite > **Note:** See the [backend configuration] documentation for details. +#### 3.2.2 Instructions for other engines (CentOS) + +See appendices for [MariaDB][MariaDB instructions for CentOS] and +[PostgreSQL][PostgreSQL instructions for CentOS]. + + ### 3.3 Service configuration and startup (CentOS) Make sure our tmpfiles configuration takes effect: @@ -316,70 +250,15 @@ 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 upgrade and want to keep the database, go to [section 7][Upgrade +database] first. If you instead want to start from afresh, then go to [appendix +C][Cleaning 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 and Ubuntu) - -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): - -```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 and Ubuntu) - -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. - -Initialize Zonemaster database (unless you keep an old database): - -```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.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.3 Instructions for SQLite (Debian and Ubuntu) +#### 4.2.1 Instructions for SQLite (Debian and Ubuntu) > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. @@ -407,6 +286,12 @@ sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite > **Note:** See the [backend configuration] documentation for details. +#### 4.2.2 Instructions for other engines (Debian and Ubuntu) + +See appendices for [MariaDB][MariaDB instructions for Debian] and +[PostgreSQL][PostgreSQL instructions for Debian]. + + ### 4.3 Service configuration and startup (Debian and Ubuntu) Make sure our tmpfiles configuration takes effect: @@ -486,15 +371,182 @@ 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 upgrade and want to keep the database, go to [section 7][Upgrade +database] first. If you instead want to start from afresh, then go to [appendix +C][Cleaning 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 MySQL (FreeBSD) +#### 5.2.1 Instructions for SQLite (FreeBSD) + +> **Note:** Zonemaster with SQLite is not meant for an installation with heavy +> load. + +> All binaries and Perl bindings are already installed. + +Configure Zonemaster::Backend to use the correct database engine and database +path: + +```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 +``` + +Create database directory, set correct ownership and create database: + +```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" +``` + +> SQLite will not run as a daemon and does not need to be started. + +> **Note:** See the [backend configuration] documentation for details. + +#### 5.2.2 Instructions for other engines (FreeBSD) + +See appendices for [MariaDB][MariaDB instructions for FreeBSD] and +[PostgreSQL][PostgreSQL instructions for FreeBSD]. + + +### 5.3 Service startup (FreeBSD) + +Enable services at startup: + +```sh +sysrc zm_rpcapi_enable="YES" +sysrc zm_testagent_enable="YES" +``` + +Start services: + +```sh +service zm_rpcapi start +service zm_testagent start +``` + +### 5.4 Post-installation (FreeBSD) + +To check the running daemons run: + +```sh +service mysql-server status # If mysql-server is installed +``` +```sh +service postgresql status # If postgresql is installed +``` +```sh +service zm_rpcapi status +service zm_testagent status +``` + +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. + +```sh +zmtest zonemaster.net +``` + +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. Upgrade Zonemaster database + +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. + +For SQLite database upgrading is not needed as of now. + + + +# Appendices + + +## A. Installation with MariaDB + +* [CentOS](#a1-mariadb-installation-for-centos) +* [Debian/Ubuntu](#a2-mariadb-installation-for-debian-and-ubuntu) +* [FreeBSD](#a3-mysql-installation-for-freebsd) + +### A.1. MariaDB installation for 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. + + +### A.2. MariaDB installation for Debian and Ubuntu + +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): + +```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. + + +### A.3. MySQL installation for FreeBSD + +> MySQL is used on FreeBSD Configure Zonemaster::Backend to use the correct database engine: @@ -549,116 +601,112 @@ mysql -u root -p < ./initial-mysql.sql > called "zonemaster" with the password "zonemaster" (as stated in the config > file). This user has just enough permissions to run the backend software. -#### 5.2.2 Instructions for PostgreSQL (FreeBSD) -Configure Zonemaster::Backend to use the correct database engine: +## B. Installation with PostgreSQL -```sh -sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= PostgreSQL/' /usr/local/etc/zonemaster/backend_config.ini -``` -> **Note:** See the [backend configuration] documentation for details. +* [CentOS](#b1-postgresql-installation-for-centos) +* [Debian/Ubuntu](#b2-postgresql-installation-for-debian-and-ubuntu) +* [FreeBSD](#b3-postgresql-installation-for-freebsd) -Install, configure and start database engine (and Perl bindings): -```sh -pkg install postgresql12-server p5-DBD-Pg -sysrc postgresql_enable="YES" -service postgresql initdb -service postgresql start -``` +### B.1. PostgreSQL installation for CentOS -Initialize Zonemaster database (unless you keep an old database): +Configure Zonemaster::Backend to use the correct database engine: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -psql -U postgres -f ./initial-postgres.sql +sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini ``` -#### 5.2.3 Instructions for SQLite (FreeBSD) +> **Note:** See the [backend configuration] documentation for details. -> **Note:** Zonemaster with SQLite is not meant for an installation with heavy -> load. +Install, configure and start database engine: -> All binaries and Perl bindings are already installed. +* On CentOS 7: -Configure Zonemaster::Backend to use the correct database engine and database -path: + ```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 + ``` -```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 -``` +* On CentOS 8: -Create database directory, set correct ownership and create database: + ```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 + ``` + +Initialize Zonemaster database (unless you keep an old database): ```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" +sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql ``` -> SQLite will not run as a daemon and does not need to be started. +> **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. -### 5.3 Service startup (FreeBSD) +### B.2. PostgreSQL installation for Debian and Ubuntu -Enable services at startup: +Configure Zonemaster::Backend to use the correct database engine: ```sh -sysrc zm_rpcapi_enable="YES" -sysrc zm_testagent_enable="YES" +sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.ini ``` -Start services: +Install the database engine and Perl bindings: ```sh -service zm_rpcapi start -service zm_testagent start +sudo apt install postgresql libdbd-pg-perl ``` -### 5.4 Post-installation (FreeBSD) +> **Note:** See the [backend configuration] documentation for details. -To check the running daemons run: +Initialize Zonemaster database (unless you keep an old database): ```sh -service mysql-server status # If mysql-server is installed -``` -```sh -service postgresql status # If postgresql is installed -``` -```sh -service zm_rpcapi status -service zm_testagent status +sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql ``` -See the [post-installation] section for post-installation matters. - +> **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. -## 6. Post-installation -### 6.1 Smoke test +### B.3. PostgreSQL installation for FreeBSD -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. +Configure Zonemaster::Backend to use the correct database engine: ```sh -zmtest zonemaster.net +sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= PostgreSQL/' /usr/local/etc/zonemaster/backend_config.ini ``` +> **Note:** See the [backend configuration] documentation for details. -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. +Install, configure and start database engine (and Perl bindings): +```sh +pkg install postgresql12-server p5-DBD-Pg +sysrc postgresql_enable="YES" +service postgresql initdb +service postgresql start +``` -### 6.2. What to do next? +Initialize Zonemaster database (unless you keep an old database): -* 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. +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +psql -U postgres -f ./initial-postgres.sql +``` -### 6.3. Cleaning up the database +## C. 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` @@ -666,41 +714,40 @@ 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). -#### 6.3.1 MySQL +### C.1. MySQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` mysql --user=root --password < ./cleanup-mysql.sql ``` -#### 6.3.2 PostgreSQL +### C.2. PostgreSQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! ``` -#### 6.3.3 SQLite +### C.3. SQLite Remove the database file and recreate it following the installation instructions above. -### 6.4 Upgrade Zonemaster database - -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. - -For SQLite database upgrading is not needed as of now. - ------- [Backend configuration]: Configuration.md +[Cleaning database]: #c-cleaning-up-the-database [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 +[MariaDB instructions for CentOS]: #a1-mariadb-installation-for-centos +[MariaDB instructions for Debian]: #a2-mariadb-installation-for-debian-and-ubuntu +[MariaDB instructions for FreeBSD]: #a3-mysql-installation-for-freebsd +[PostgreSQL instructions for CentOS]: #b1-postgresql-installation-for-centos +[PostgreSQL instructions for Debian]: #b2-postgresql-installation-for-debian-and-ubuntu +[PostgreSQL instructions for FreeBSD]: #b3-postgresql-installation-for-freebsd +[Post-installation]: #6-post-installation [README.md-upgrade]: /README.md#upgrade +[Upgrade database]: #7-upgrade-zonemaster-database [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 From 9053a309a21d9c57e8280adf6c1272119c56b04a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 13:52:47 +0200 Subject: [PATCH 214/424] Remove the "queue" parameter from fingerprint Revert commit c18417c and remove unit test from commit 12b8bff. --- docs/API.md | 4 ++-- lib/Zonemaster/Backend/DB.pm | 3 --- t/db.t | 18 ++---------------- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/docs/API.md b/docs/API.md index dcb828ce5..377dfea64 100644 --- a/docs/API.md +++ b/docs/API.md @@ -691,8 +691,8 @@ the `test id` of the previous test will be returned. The default value of 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`, -`profile` and `queue`. +considered to be the same are `domain`, `ipv6`, `ipv4`, `nameservers`, `ds_info` +and `profile`. #### `"error"` diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index e1069ff15..90993c400 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -190,13 +190,10 @@ sub _project_params { my %projection = (); - # some of these values are already set in RPCAPI - # however setting them here again is required for testing purpose $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"; - $projection{queue} = $$params{queue} // 0; my $array_ds_info = $$params{ds_info} // []; my @array_ds_info_sort = sort { diff --git a/t/db.t b/t/db.t index 5de3c332d..6ee7bb675 100644 --- a/t/db.t +++ b/t/db.t @@ -19,7 +19,7 @@ sub encode_and_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","queue":0}'; + my $expected_encoded_params = '{"domain":"example.com","ds_info":[],"ipv4":true,"ipv6":true,"nameservers":[],"profile":"default"}'; my %params = ( domain => "example.com" ); @@ -120,7 +120,7 @@ subtest 'encoding and fingerprint' => sub { }; 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","queue":0}'; + 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", ); @@ -150,20 +150,6 @@ subtest 'encoding and fingerprint' => sub { isnt $fingerprint1, $fingerprint2, 'different profiles, different fingerprints'; }; - subtest 'different queues' => sub { - my %params1 = ( - domain => "example.com", - queue => 1 - ); - my %params2 = ( - domain => "example.com", - queue => 2 - ); - my ( undef, $fingerprint1 ) = encode_and_fingerprint( \%params1 ); - my ( undef, $fingerprint2 ) = encode_and_fingerprint( \%params2 ); - - isnt $fingerprint1, $fingerprint2, 'different queues, different fingerprints'; - }; } }; From a19faae76144524515b1377a43465502638c9288 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 13:54:57 +0200 Subject: [PATCH 215/424] Add unit test on fingerprint --- t/db.t | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/db.t b/t/db.t index 6ee7bb675..c3c460632 100644 --- a/t/db.t +++ b/t/db.t @@ -150,6 +150,22 @@ subtest 'encoding and fingerprint' => sub { 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'; + }; } }; From 3ca10bb3fd44b9835a4fcadba4e8abf427f8ac85 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 6 Sep 2021 16:59:42 +0200 Subject: [PATCH 216/424] Editorial updates --- docs/Installation.md | 66 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index c101c80ca..0ac77bb6f 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -9,11 +9,11 @@ * [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 (and Ubuntu)](#4-installation-on-debian-and-ubuntu) - * [4.1 Install Zonemaster::Backend and related dependencies (Debian/Ubuntu)](#41-install-zonemasterbackend-and-related-dependencies-debian-and-ubuntu) - * [4.2 Database engine installation and configuration (Debian/Ubuntu)](#42-database-engine-installation-and-configuration-debian-and-ubuntu) - * [4.3 Service configuration and startup (Debian/Ubuntu)](#43-service-configuration-and-startup-debian-and-ubuntu) - * [4.4 Post-installation (Debian/Ubuntu)](#44-post-installation-debian-and-ubuntu) +* [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 and configuration (Debian/Ubuntu)](#42-database-engine-installation-and-configuration-debianubuntu) + * [4.3 Service configuration and startup (Debian/Ubuntu)](#43-service-configuration-and-startup-debianubuntu) + * [4.4 Post-installation (Debian/Ubuntu)](#44-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) @@ -176,7 +176,7 @@ See the [post-installation] section for post-installation matters. ## 4. Installation on Debian and Ubuntu -### 4.1 Install Zonemaster::Backend and related dependencies (Debian and Ubuntu) +### 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. @@ -244,7 +244,7 @@ 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 and Ubuntu) +### 4.2 Database engine installation and configuration (Debian/Ubuntu) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. @@ -258,7 +258,7 @@ 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 SQLite (Debian and Ubuntu) +#### 4.2.1 Instructions for SQLite (Debian/Ubuntu) > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. @@ -286,13 +286,13 @@ sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite > **Note:** See the [backend configuration] documentation for details. -#### 4.2.2 Instructions for other engines (Debian and Ubuntu) +#### 4.2.2 Instructions for other engines (Debian/Ubuntu) See appendices for [MariaDB][MariaDB instructions for Debian] and [PostgreSQL][PostgreSQL instructions for Debian]. -### 4.3 Service configuration and startup (Debian and Ubuntu) +### 4.3 Service configuration and startup (Debian/Ubuntu) Make sure our tmpfiles configuration takes effect: @@ -310,7 +310,7 @@ sudo systemctl start zm-testagent ``` -### 4.4 Post-installation (Debian and Ubuntu) +### 4.4 Post-installation (Debian/Ubuntu) See the [post-installation] section for post-installation matters. @@ -431,12 +431,6 @@ service zm_testagent start To check the running daemons run: -```sh -service mysql-server status # If mysql-server is installed -``` -```sh -service postgresql status # If postgresql is installed -``` ```sh service zm_rpcapi status service zm_testagent status @@ -479,16 +473,16 @@ For SQLite database upgrading is not needed as of now. -# Appendices +## Appendices -## A. Installation with MariaDB +### A. Installation with MariaDB -* [CentOS](#a1-mariadb-installation-for-centos) -* [Debian/Ubuntu](#a2-mariadb-installation-for-debian-and-ubuntu) -* [FreeBSD](#a3-mysql-installation-for-freebsd) +* [CentOS](#a1-mariadb-installation-on-centos) +* [Debian/Ubuntu](#a2-mariadb-installation-on-debian-and-ubuntu) +* [FreeBSD](#a3-mysql-installation-on-freebsd) -### A.1. MariaDB installation for CentOS +#### A.1. MariaDB installation on CentOS Configure Zonemaster::Backend to use the correct database engine: @@ -517,7 +511,7 @@ sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backe > file). This user has just enough permissions to run the backend software. -### A.2. MariaDB installation for Debian and Ubuntu +#### A.2. MariaDB installation on Debian and Ubuntu Install the database engine and its dependencies: @@ -544,7 +538,7 @@ sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backe > file). This user has just enough permissions to run the backend software. -### A.3. MySQL installation for FreeBSD +#### A.3. MySQL installation on FreeBSD > MySQL is used on FreeBSD @@ -602,14 +596,14 @@ mysql -u root -p < ./initial-mysql.sql > file). This user has just enough permissions to run the backend software. -## B. Installation with PostgreSQL +### B. Installation with PostgreSQL -* [CentOS](#b1-postgresql-installation-for-centos) -* [Debian/Ubuntu](#b2-postgresql-installation-for-debian-and-ubuntu) -* [FreeBSD](#b3-postgresql-installation-for-freebsd) +* [CentOS](#b1-postgresql-installation-on-centos) +* [Debian/Ubuntu](#b2-postgresql-installation-on-debian-and-ubuntu) +* [FreeBSD](#b3-postgresql-installation-on-freebsd) -### B.1. PostgreSQL installation for CentOS +#### B.1. PostgreSQL installation on CentOS Configure Zonemaster::Backend to use the correct database engine: @@ -653,7 +647,7 @@ sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zone > This user has just enough permissions to run the backend software. -### B.2. PostgreSQL installation for Debian and Ubuntu +#### B.2. PostgreSQL installation on Debian and Ubuntu Configure Zonemaster::Backend to use the correct database engine: @@ -680,7 +674,7 @@ sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zone > This user has just enough permissions to run the backend software. -### B.3. PostgreSQL installation for FreeBSD +#### B.3. PostgreSQL installation on FreeBSD Configure Zonemaster::Backend to use the correct database engine: @@ -706,7 +700,7 @@ psql -U postgres -f ./initial-postgres.sql ``` -## C. Cleaning up the database +### C. 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` @@ -714,21 +708,21 @@ 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). -### C.1. MySQL +#### C.1. MySQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` mysql --user=root --password < ./cleanup-mysql.sql ``` -### C.2. PostgreSQL +#### C.2. PostgreSQL ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! ``` -### C.3. SQLite +#### C.3. SQLite Remove the database file and recreate it following the installation instructions above. From 3ed29e2c10b3480a51a15ed4d0cb6650143f07f1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 16:43:50 +0200 Subject: [PATCH 217/424] Remove sentence on SQLite database upgrade --- docs/Installation.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 0ac77bb6f..498601dd7 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -469,8 +469,6 @@ 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. -For SQLite database upgrading is not needed as of now. - ## Appendices From be02bdbe82bd19c23fbb4b545464a96701def4e0 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 16:51:33 +0200 Subject: [PATCH 218/424] Set SQLite as default database engine --- docs/Installation.md | 18 +----------------- share/backend_config.ini | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 498601dd7..78ea74af6 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -126,13 +126,6 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -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 -``` - Create database directory, set correct ownership and create database: ```sh @@ -265,13 +258,6 @@ but if you have removed the old Zonemaster database, then do the initialization. > 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 -``` - Create database directory, set correct ownership and create database: ```sh @@ -385,11 +371,9 @@ but if you have removed the old Zonemaster database, then do the initialization. > All binaries and Perl bindings are already installed. -Configure Zonemaster::Backend to use the correct database engine and database -path: +Configure Zonemaster::Backend to use the correct database path: ```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 ``` diff --git a/share/backend_config.ini b/share/backend_config.ini index 39474fe75..daf586ec9 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] From 7a4bd96bb8bc44314545c6e74c038994cea69112 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 16:52:21 +0200 Subject: [PATCH 219/424] Refactor installation with SQLite --- docs/Installation.md | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 78ea74af6..0dbf0266c 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -126,7 +126,7 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -Create database directory, set correct ownership and create database: +Create database directory, database and set correct ownership: ```sh cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` @@ -135,8 +135,6 @@ sudo perl create_db_sqlite.pl sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite ``` -> **Note:** See the [backend configuration] documentation for details. - #### 3.2.2 Instructions for other engines (CentOS) @@ -256,9 +254,7 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -> All binaries and Perl bindings are already installed. - -Create database directory, set correct ownership and create database: +Create database directory, database and set correct ownership: ```sh cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` @@ -267,10 +263,6 @@ sudo perl create_db_sqlite.pl sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite ``` -> SQLite will not run as a daemon and does not need to be started. - -> **Note:** See the [backend configuration] documentation for details. - #### 4.2.2 Instructions for other engines (Debian/Ubuntu) @@ -369,15 +361,15 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -> All binaries and Perl bindings are already installed. - Configure Zonemaster::Backend to use the correct database path: ```sh sed -i '' '/[[:<:]]database_file[[:>:]]/ s:=.*:= /var/db/zonemaster/db.sqlite:' /usr/local/etc/zonemaster/backend_config.ini ``` -Create database directory, set correct ownership and create database: +> **Note:** See the [backend configuration] documentation for details. + +Create database directory and database with correct ownership: ```sh cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` @@ -385,10 +377,6 @@ 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" ``` -> SQLite will not run as a daemon and does not need to be started. - -> **Note:** See the [backend configuration] documentation for details. - #### 5.2.2 Instructions for other engines (FreeBSD) See appendices for [MariaDB][MariaDB instructions for FreeBSD] and From 9aa84c5b577203de512b9748f69882136e15220e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 8 Sep 2021 17:51:39 +0200 Subject: [PATCH 220/424] Add a note to point to the Configuration document --- docs/Installation.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 0dbf0266c..f4b1bafb9 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -135,6 +135,9 @@ sudo perl create_db_sqlite.pl sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite ``` +> Some parameters can be changed, see the [backend configuration] documentation +> for details. + #### 3.2.2 Instructions for other engines (CentOS) @@ -263,6 +266,9 @@ sudo perl create_db_sqlite.pl sudo chown zonemaster:zonemaster /var/lib/zonemaster/db.sqlite ``` +> Some parameters can be changed, see the [backend configuration] documentation +> for details. + #### 4.2.2 Instructions for other engines (Debian/Ubuntu) @@ -367,8 +373,6 @@ Configure Zonemaster::Backend to use the correct database path: 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. - Create database directory and database with correct ownership: ```sh @@ -377,6 +381,9 @@ 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" ``` +> Some parameters can be changed, see the [backend configuration] documentation +> for details. + #### 5.2.2 Instructions for other engines (FreeBSD) See appendices for [MariaDB][MariaDB instructions for FreeBSD] and From 2b26e7822165d2bfcb129f5d6523d0d600f0a240 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 09:45:26 +0200 Subject: [PATCH 221/424] Remove spaces and better indentation --- lib/Zonemaster/Backend/DB/SQLite.pm | 56 ++++++++++++++--------------- script/create_db_mysql.pl | 10 +++--- script/create_db_postgresql_9.3.pl | 54 +++++++++++++++------------- share/initial-mysql.sql | 2 +- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 1adc1ea60..f12ab7334 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -63,23 +63,23 @@ sub create_db { $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 - ) - ' + 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"; #################################################################### @@ -89,11 +89,11 @@ sub create_db { $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 - ) - ' + 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"; #################################################################### @@ -102,12 +102,12 @@ sub create_db { $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 - ) - ' + 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"; return 1; diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index 3c296e660..dcf53e205 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -44,7 +44,7 @@ sub create_db { ) ENGINE=InnoDB ' ); - + $dbh->do( 'CREATE TRIGGER before_insert_test_results BEFORE INSERT ON test_results @@ -61,7 +61,7 @@ sub create_db { $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)' ); @@ -69,9 +69,10 @@ sub create_db { $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 #################################################################### @@ -86,6 +87,7 @@ sub create_db { ' ); + #################################################################### # USERS #################################################################### diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index fa5bca842..7463546d2 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -40,28 +40,28 @@ sub create_db { $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, - undelegated integer NOT NULL DEFAULT 0, - results json DEFAULT NULL, - nb_retries integer NOT NULL DEFAULT 0 - ) + 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, + undelegated integer NOT NULL DEFAULT 0, + 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)' ); @@ -69,12 +69,13 @@ sub create_db { $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 #################################################################### @@ -94,14 +95,16 @@ sub create_db { $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 - ) + 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 #################################################################### @@ -121,11 +124,12 @@ sub create_db { $dbh->do( 'CREATE TABLE users ( - id integer DEFAULT nextval(\'users_id_seq\'::regclass) primary key, - user_info json DEFAULT NULL - ) + id integer DEFAULT nextval(\'users_id_seq\'::regclass) primary key, + user_info json DEFAULT NULL + ) ' ); + $dbh->do( "ALTER TABLE users OWNER TO $db_user" ); } diff --git a/share/initial-mysql.sql b/share/initial-mysql.sql index 075520f3b..b49fe5764 100644 --- a/share/initial-mysql.sql +++ b/share/initial-mysql.sql @@ -39,7 +39,7 @@ CREATE TRIGGER before_insert_test_results END IF; END// DELIMITER // - + CREATE TABLE batch_jobs ( id integer AUTO_INCREMENT PRIMARY KEY, username character varying(50) NOT NULL, From 6756f44f1551f9a3032e4e77229b95d9567b182e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 09:46:36 +0200 Subject: [PATCH 222/424] Consistent MySQL database initialization * "results" column was changed to MEDIUMBLOB with commit a028b32 (see issue #616). * "undelegated" column definition and default value was updated with commit 37a371b (see issue #812) * missing index creation on "progress" This applies to the Perl initialization script. --- script/create_db_mysql.pl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index dcf53e205..b67e60d37 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -38,8 +38,8 @@ sub create_db { progress integer DEFAULT 0, params_deterministic_hash character varying(32), params blob NOT NULL, - results blob DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, + results mediumblob DEFAULT NULL, + undelegated integer NOT NULL DEFAULT 0, nb_retries integer NOT NULL DEFAULT 0 ) ENGINE=InnoDB ' @@ -61,16 +61,18 @@ sub create_db { $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)" ); + $dbh->do( + 'CREATE INDEX test_results__progress ON test_results (progress)' + ); + $dbh->do( + 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' + ); #################################################################### From 18f0061fbc83827c65e790261e7aa1b223dbc58d Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 11:24:05 +0200 Subject: [PATCH 223/424] PostgreSQL: remove dead code This code has been around since at least 2012 (see commit ab54029) --- script/create_db_postgresql_9.3.pl | 3 --- 1 file changed, 3 deletions(-) diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 7463546d2..10d06f9a4 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -21,9 +21,6 @@ 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' ); From 4698dd403d9373f205c9ca6005d6fd31876512d0 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 11:32:28 +0200 Subject: [PATCH 224/424] PostgreSQL: remove SEQUENCE usage --- script/create_db_postgresql_9.3.pl | 42 +++--------------------------- share/initial-postgres.sql | 1 - 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 10d06f9a4..79e753261 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -22,22 +22,10 @@ sub create_db { # TEST RESULTS #################################################################### $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, + 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 DEFAULT NULL, creation_time timestamp without time zone DEFAULT NOW() NOT NULL, @@ -77,22 +65,10 @@ sub create_db { # 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, + id serial PRIMARY KEY, username character varying(50) NOT NULL, creation_time timestamp without time zone DEFAULT NOW() NOT NULL ) @@ -106,22 +82,10 @@ sub create_db { # 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, + id serial PRIMARY KEY, user_info json DEFAULT NULL ) ' diff --git a/share/initial-postgres.sql b/share/initial-postgres.sql index f28d17f9f..d7c5f5563 100644 --- a/share/initial-postgres.sql +++ b/share/initial-postgres.sql @@ -41,4 +41,3 @@ CREATE TABLE users ( 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; From 44639fa7cf20c0df8107c1ded8a53c0d942224c7 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 11:34:25 +0200 Subject: [PATCH 225/424] Consistent PostgreSQL database initialization * remove some default values * update some column definition * missing index creation on "progress" This applies to the Perl initialization script based on the ``initial-postgres.sql'' file. --- script/create_db_postgresql_9.3.pl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 79e753261..60c22fbff 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -27,17 +27,17 @@ sub create_db { '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 DEFAULT 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, + 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 character varying(32), + params_deterministic_hash varchar(32), params json NOT NULL, undelegated integer NOT NULL DEFAULT 0, - results json DEFAULT NULL, + results json, nb_retries integer NOT NULL DEFAULT 0 ) ' @@ -46,16 +46,18 @@ sub create_db { $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( + '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( "ALTER TABLE test_results OWNER TO $db_user" ); @@ -69,7 +71,7 @@ sub create_db { $dbh->do( 'CREATE TABLE batch_jobs ( id serial PRIMARY KEY, - username character varying(50) NOT NULL, + username varchar(50) NOT NULL, creation_time timestamp without time zone DEFAULT NOW() NOT NULL ) ' @@ -86,7 +88,7 @@ sub create_db { $dbh->do( 'CREATE TABLE users ( id serial PRIMARY KEY, - user_info json DEFAULT NULL + user_info json ) ' ); From 2854824d4b9e411d87008fd7a9343eab6c238e98 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 28 Jul 2021 16:47:53 +0200 Subject: [PATCH 226/424] Reset MySQL delimiter --- share/initial-mysql.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/initial-mysql.sql b/share/initial-mysql.sql index b49fe5764..cbb21a40b 100644 --- a/share/initial-mysql.sql +++ b/share/initial-mysql.sql @@ -38,13 +38,15 @@ CREATE TRIGGER before_insert_test_results SET new.hash_id = SUBSTRING(MD5(CONCAT(RAND(), UUID())) from 1 for 16); END IF; END// -DELIMITER // + +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), From 1b5c92ae7d30fa4013ba19d97c3368a25afa4eba Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 06:53:49 +0200 Subject: [PATCH 227/424] Update installation instruction for MySQL Move database and user creations to the installation documentation instead of within the initial-mysql.sql file. --- docs/Installation.md | 61 +++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index f4b1bafb9..99f5eddd9 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -477,15 +477,25 @@ sudo systemctl enable mariadb sudo systemctl start mariadb ``` -Initialize the database (unless you keep an old database): +To initialize the database (unless you keep an old database) connect to the +MariaDB server: ```sh -sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-mysql.sql +sudo mysql ``` -> **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. +Create the database: +```sql +CREATE DATABASE zonemaster; +``` + +Create a new user and give it all permissions on the newly created database: +```sql +CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; +GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; +``` + +Then update the `/etc/zonemaster/backend_config.ini` file accordingly. #### A.2. MariaDB installation on Debian and Ubuntu @@ -504,15 +514,25 @@ 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): +To initialize the database (unless you keep an old database) connect to the +MariaDB server: ```sh -sudo mysql < $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-mysql.sql +sudo mysql +``` + +Create the database: +```sql +CREATE DATABASE zonemaster; +``` + +Create a new user and give it all permissions on the newly created database: +```sql +CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; +GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; ``` -> **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. +Then update the `/etc/zonemaster/backend_config.ini` file accordingly. #### A.3. MySQL installation on FreeBSD @@ -554,23 +574,24 @@ Reset root password in MySQL (required by MySQL). Replace ALTER USER 'root'@'localhost' IDENTIFIED BY ''; ``` -Logout from database: +Unless you keep an old database, initialize the database: +```sql +CREATE DATABASE zonemaster; +``` +Create a new user and give it all permissions on the newly created database: ```sql -exit; +CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; +GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; ``` -Unless you keep an old database, initialize the database (and give the -root password when prompted): +Logout from database: -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -mysql -u root -p < ./initial-mysql.sql +```sql +exit; ``` -> **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. +Then update the `/etc/zonemaster/backend_config.ini` file accordingly. ### B. Installation with PostgreSQL From 2fee60b6c2e58d30c3bfa2f1e658026c636766d6 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:06:21 +0200 Subject: [PATCH 228/424] Update installation instruction for PostgreSQL Move database and user creations to the installation documentation instead of within the initial-postgres.sql file. --- docs/Installation.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 99f5eddd9..25becc966 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -634,15 +634,17 @@ Install, configure and start database engine: sudo systemctl start postgresql ``` -Initialize Zonemaster database (unless you keep an old database): +Initialize Zonemaster database and user (unless you keep an old database): ```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql +sudo -u postgres createuser -P 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. +This will ask for a password that should be copied in `backend_config.ini`. + +```sh +sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster +``` #### B.2. PostgreSQL installation on Debian and Ubuntu @@ -661,15 +663,17 @@ sudo apt install postgresql libdbd-pg-perl > **Note:** See the [backend configuration] documentation for details. -Initialize Zonemaster database (unless you keep an old database): +Initialize Zonemaster database and user (unless you keep an old database): ```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/initial-postgres.sql +sudo -u postgres createuser -P 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. +This will ask for a password that should be copied in `backend_config.ini`. + +```sh +sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster +``` #### B.3. PostgreSQL installation on FreeBSD @@ -690,11 +694,16 @@ service postgresql initdb service postgresql start ``` -Initialize Zonemaster database (unless you keep an old database): +Initialize Zonemaster database and user (unless you keep an old database): ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -psql -U postgres -f ./initial-postgres.sql +sudo -u postgres createuser -P zonemaster +``` + +This will ask for a password that should be copied in `backend_config.ini`. + +```sh +sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` From 69c3b10afdf1f0c5ef3cbd9c02a812d7dbcdd699 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:43:42 +0200 Subject: [PATCH 229/424] Do not DROP tables on creation --- lib/Zonemaster/Backend/DB/SQLite.pm | 7 ------- script/create_db_mysql.pl | 8 -------- script/create_db_postgresql_9.3.pl | 6 ------ 3 files changed, 21 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index f12ab7334..5e14d3400 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -57,10 +57,6 @@ sub create_db { #################################################################### # 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"; - $self->dbh->do( 'CREATE TABLE test_results ( id integer PRIMARY KEY AUTOINCREMENT, @@ -85,8 +81,6 @@ sub create_db { #################################################################### # BATCH JOBS #################################################################### - $self->dbh->do( 'DROP TABLE IF EXISTS batch_jobs' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; - $self->dbh->do( 'CREATE TABLE batch_jobs ( id integer PRIMARY KEY, @@ -99,7 +93,6 @@ sub create_db { #################################################################### # USERS #################################################################### - $self->dbh->do( 'DROP TABLE IF EXISTS users' ); $self->dbh->do( 'CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index b67e60d37..fde7b16da 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -20,10 +20,6 @@ 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, @@ -78,8 +74,6 @@ sub create_db { #################################################################### # BATCH JOBS #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS batch_jobs CASCADE' ); - $dbh->do( 'CREATE TABLE batch_jobs ( id integer AUTO_INCREMENT PRIMARY KEY, @@ -93,8 +87,6 @@ sub create_db { #################################################################### # USERS #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS users CASCADE' ); - $dbh->do( 'CREATE TABLE users ( id integer AUTO_INCREMENT primary key, diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 60c22fbff..8dc0e79d0 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -21,8 +21,6 @@ sub create_db { #################################################################### # TEST RESULTS #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS test_results CASCADE' ); - $dbh->do( 'CREATE TABLE test_results ( id serial PRIMARY KEY, @@ -66,8 +64,6 @@ sub create_db { #################################################################### # BATCH JOBS #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS batch_jobs CASCADE' ); - $dbh->do( 'CREATE TABLE batch_jobs ( id serial PRIMARY KEY, @@ -83,8 +79,6 @@ sub create_db { #################################################################### # USERS #################################################################### - $dbh->do( 'DROP TABLE IF EXISTS users CASCADE' ); - $dbh->do( 'CREATE TABLE users ( id serial PRIMARY KEY, From 65f7d5c47aa664caaf661a0d83b115fe96e1fb83 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:47:33 +0200 Subject: [PATCH 230/424] Move create_db into Zonemaster::Backend::DB::MySQL --- lib/Zonemaster/Backend/DB/MySQL.pm | 86 +++++++++++++++++++++++++++++ script/create_db_mysql.pl | 88 +----------------------------- 2 files changed, 88 insertions(+), 86 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 90ce33fc8..9027c70a5 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -95,6 +95,92 @@ sub dbh { return $self->dbhandle; } +sub create_db { + my ( $self ) = @_; + + my $dbh = $self->dbh; + + #################################################################### + # TEST RESULTS + #################################################################### + $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 mediumblob DEFAULT NULL, + undelegated integer NOT NULL DEFAULT 0, + 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__progress ON test_results (progress)' + ); + $dbh->do( + 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' + ); + + + #################################################################### + # BATCH JOBS + #################################################################### + $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( + 'CREATE TABLE users ( + id integer AUTO_INCREMENT primary key, + username varchar(128), + api_key varchar(512), + user_info blob DEFAULT NULL + ) ENGINE=InnoDB; + ' + ); +} + sub user_exists_in_db { my ( $self, $user ) = @_; diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index fde7b16da..d267da752 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -13,89 +13,5 @@ if ( $config->DB_engine ne 'MySQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::MySQL->from_config( $config )->dbh; - -sub create_db { - - #################################################################### - # TEST RESULTS - #################################################################### - $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 mediumblob DEFAULT NULL, - undelegated integer NOT NULL DEFAULT 0, - 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__progress ON test_results (progress)' - ); - $dbh->do( - 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' - ); - - - #################################################################### - # BATCH JOBS - #################################################################### - $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( - '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(); +my $db = Zonemaster::Backend::DB::MySQL->from_config( $config ); +$db->create_db(); From 97f149bb828e71eb873439a6342be0de36123459 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:47:58 +0200 Subject: [PATCH 231/424] Move create_db into Zonemaster::Backend::DB::PostgreSQL --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 71 ++++++++++++++++++++++ script/create_db_postgresql_9.3.pl | 81 +------------------------ 2 files changed, 73 insertions(+), 79 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index ac9b0a7a1..d14bb57a8 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -91,6 +91,77 @@ sub dbh { return $self->dbhandle; } +sub create_db { + my ( $self ) = @_; + + my $dbh = $self->dbh; + + #################################################################### + # TEST RESULTS + #################################################################### + $dbh->do( + '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, + undelegated integer NOT NULL DEFAULT 0, + results json, + 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__progress ON test_results (progress)' + ); + $dbh->do( + "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated'))" + ); + + + #################################################################### + # BATCH JOBS + #################################################################### + $dbh->do( + 'CREATE TABLE batch_jobs ( + id serial PRIMARY KEY, + username varchar(50) NOT NULL, + creation_time timestamp without time zone DEFAULT NOW() NOT NULL + ) + ' + ); + + + #################################################################### + # USERS + #################################################################### + $dbh->do( + 'CREATE TABLE users ( + id serial PRIMARY KEY, + user_info json + ) + ' + ); + +} + sub user_exists_in_db { my ( $self, $user ) = @_; diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 8dc0e79d0..84c80c18f 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -13,82 +13,5 @@ if ( $config->DB_engine ne 'PostgreSQL' ) { die "The configuration file does not contain the MySQL backend"; } -my $dbh = Zonemaster::Backend::DB::PostgreSQL->from_config( $config )->dbh; -my $db_user = $config->POSTGRESQL_user; - -sub create_db { - - #################################################################### - # TEST RESULTS - #################################################################### - $dbh->do( - '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, - undelegated integer NOT NULL DEFAULT 0, - results json, - 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__progress ON test_results (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( - 'CREATE TABLE batch_jobs ( - id serial PRIMARY KEY, - username varchar(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( - 'CREATE TABLE users ( - id serial PRIMARY KEY, - user_info json - ) - ' - ); - - $dbh->do( "ALTER TABLE users OWNER TO $db_user" ); - -} - -create_db(); +my $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); +$db->create_db(); From c3bb3b3fe6550feb236bf13d33bba3cbe385800c Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:48:12 +0200 Subject: [PATCH 232/424] Remove unused modules --- script/create_db_mysql.pl | 5 ----- script/create_db_postgresql_9.3.pl | 5 ----- share/create_db_sqlite.pl | 5 ----- 3 files changed, 15 deletions(-) diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl index d267da752..65d3674f9 100644 --- a/script/create_db_mysql.pl +++ b/script/create_db_mysql.pl @@ -1,10 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); use Zonemaster::Backend::Config; use Zonemaster::Backend::DB::MySQL; diff --git a/script/create_db_postgresql_9.3.pl b/script/create_db_postgresql_9.3.pl index 84c80c18f..670f6727d 100644 --- a/script/create_db_postgresql_9.3.pl +++ b/script/create_db_postgresql_9.3.pl @@ -1,10 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); use Zonemaster::Backend::Config; use Zonemaster::Backend::DB::PostgreSQL; diff --git a/share/create_db_sqlite.pl b/share/create_db_sqlite.pl index f578e1f94..466bb4529 100644 --- a/share/create_db_sqlite.pl +++ b/share/create_db_sqlite.pl @@ -1,10 +1,5 @@ use strict; use warnings; -use utf8; -use Data::Dumper; -use Encode; - -use DBI qw(:utils); use Zonemaster::Backend::Config; use Zonemaster::Backend::DB::SQLite; From 0087d33b4e6cddb676734382312e886584a0b1b1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 07:56:14 +0200 Subject: [PATCH 233/424] New generic create_db call * Remove each script per database engine * Update MANIFEST --- MANIFEST | 4 +--- script/create_db_mysql.pl | 12 ------------ script/create_db_postgresql_9.3.pl | 12 ------------ share/create_db.pl | 13 +++++++++++++ share/create_db_sqlite.pl | 12 ------------ 5 files changed, 14 insertions(+), 39 deletions(-) delete mode 100644 script/create_db_mysql.pl delete mode 100644 script/create_db_postgresql_9.3.pl create mode 100644 share/create_db.pl delete mode 100644 share/create_db_sqlite.pl diff --git a/MANIFEST b/MANIFEST index 7088656d2..ede55d3d8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -40,8 +40,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 @@ -49,7 +47,7 @@ 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 diff --git a/script/create_db_mysql.pl b/script/create_db_mysql.pl deleted file mode 100644 index 65d3674f9..000000000 --- a/script/create_db_mysql.pl +++ /dev/null @@ -1,12 +0,0 @@ -use strict; -use warnings; - -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 ); -$db->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 670f6727d..000000000 --- a/script/create_db_postgresql_9.3.pl +++ /dev/null @@ -1,12 +0,0 @@ -use strict; -use warnings; - -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 $db = Zonemaster::Backend::DB::PostgreSQL->from_config( $config ); -$db->create_db(); diff --git a/share/create_db.pl b/share/create_db.pl new file mode 100644 index 000000000..1f32b51cc --- /dev/null +++ b/share/create_db.pl @@ -0,0 +1,13 @@ +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 466bb4529..000000000 --- a/share/create_db_sqlite.pl +++ /dev/null @@ -1,12 +0,0 @@ -use strict; -use warnings; - -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 ); -$db->create_db(); From 597e6d595805a8ec2df922c8ef9da761c7f02b45 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 08:01:53 +0200 Subject: [PATCH 234/424] Refactor and adapt Travis --- .travis.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd0afc76f..ab8c776c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -112,17 +112,15 @@ before_install: - 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 From a53017ecdd67a79119e0999b4ac6d1c78bc30d11 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 08:46:55 +0200 Subject: [PATCH 235/424] Remove unused SQL files * Database initialization is now performed via create_db.pl * Update MANIFEST --- MANIFEST | 2 -- share/initial-mysql.sql | 59 -------------------------------------- share/initial-postgres.sql | 43 --------------------------- 3 files changed, 104 deletions(-) delete mode 100644 share/initial-mysql.sql delete mode 100644 share/initial-postgres.sql diff --git a/MANIFEST b/MANIFEST index ede55d3d8..3341e07a8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -49,8 +49,6 @@ share/cleanup-mysql.sql share/cleanup-postgres.sql 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 diff --git a/share/initial-mysql.sql b/share/initial-mysql.sql deleted file mode 100644 index cbb21a40b..000000000 --- a/share/initial-mysql.sql +++ /dev/null @@ -1,59 +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 integer NOT NULL DEFAULT 0, - 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 d7c5f5563..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, - undelegated integer NOT NULL DEFAULT 0, - 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; From ded7b90b66e570b0b3d8c1e2af90b6ef0ff8aab5 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 08:43:52 +0200 Subject: [PATCH 236/424] Update documentation installation process * Call to create_db script for each database * Refactor SQLite initialization --- docs/Installation.md | 56 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 25becc966..1f445abf0 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -129,10 +129,9 @@ but if you have removed the old Zonemaster database, then do the initialization. Create database directory, database and set correct ownership: ```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 +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -260,10 +259,9 @@ but if you have removed the old Zonemaster database, then do the initialization. Create database directory, database and set correct ownership: ```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 +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -376,9 +374,9 @@ sed -i '' '/[[:<:]]database_file[[:>:]]/ s:=.*:= /var/db/zonemaster/db.sqlite:' Create database directory and database with correct ownership: ```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" +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +su -m zonemaster -c "perl create_db.pl" ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -497,6 +495,13 @@ GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + #### A.2. MariaDB installation on Debian and Ubuntu @@ -534,6 +539,13 @@ GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + #### A.3. MySQL installation on FreeBSD @@ -593,6 +605,13 @@ exit; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +su -m zonemaster -c "perl create_db.pl" +``` + ### B. Installation with PostgreSQL @@ -646,6 +665,13 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + #### B.2. PostgreSQL installation on Debian and Ubuntu @@ -675,6 +701,13 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + #### B.3. PostgreSQL installation on FreeBSD @@ -706,6 +739,13 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +su -m zonemaster -c "perl create_db.pl" +``` + ### C. Cleaning up the database From 78184e5fe1d544f0d5bd3079a306fdd01ab5cdf2 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 4 Aug 2021 09:19:04 +0200 Subject: [PATCH 237/424] Avoid repetition in documentation Create a new section to document database configuration once per OS. --- docs/Installation.md | 123 +++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 69 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 1f445abf0..10687f34e 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -6,19 +6,22 @@ * [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) + * [3.2 Database engine installation (CentOS)](#32-database-engine-installation-centos) + * [3.3 Database configuration (CentOS)](#33-database-configuration-centos) + * [3.4 Service configuration and startup (CentOS)](#34-service-configuration-and-startup-centos) + * [3.5 Post-installation (CentOS)](#35-post-installation-centos) * [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 and configuration (Debian/Ubuntu)](#42-database-engine-installation-and-configuration-debianubuntu) - * [4.3 Service configuration and startup (Debian/Ubuntu)](#43-service-configuration-and-startup-debianubuntu) - * [4.4 Post-installation (Debian/Ubuntu)](#44-post-installation-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) + * [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) @@ -107,7 +110,7 @@ sudo install -v -m 755 ./tmpfiles.conf /usr/lib/tmpfiles.d/zonemaster.conf > previous version of Zonemaster-Backend). -### 3.2 Database engine installation and configuration (CentOS) +### 3.2 Database engine installation (CentOS) Check the [declaration of prerequisites] to make sure your preferred combination of operating system version and database engine version is supported. @@ -126,12 +129,10 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -Create database directory, database and set correct ownership: +Create database directory: ```sh sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster -cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -144,7 +145,17 @@ See appendices for [MariaDB][MariaDB instructions for CentOS] and [PostgreSQL][PostgreSQL instructions for CentOS]. -### 3.3 Service configuration and startup (CentOS) +### 3.3 Database configuration (CentOS) + +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + + +### 3.4 Service configuration and startup (CentOS) Make sure our tmpfiles configuration takes effect: @@ -162,7 +173,7 @@ sudo systemctl start zm-testagent ``` -### 3.4 Post-installation (CentOS) +### 3.5 Post-installation (CentOS) See the [post-installation] section for post-installation matters. @@ -237,7 +248,7 @@ 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/Ubuntu) +### 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. @@ -256,12 +267,10 @@ but if you have removed the old Zonemaster database, then do the initialization. > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. -Create database directory, database and set correct ownership: +Create database directory: ```sh sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster -cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -274,7 +283,17 @@ See appendices for [MariaDB][MariaDB instructions for Debian] and [PostgreSQL][PostgreSQL instructions for Debian]. -### 4.3 Service configuration and startup (Debian/Ubuntu) +### 4.3 Database configuration (Debian/Ubuntu) + +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +sudo -u zonemaster perl create_db.pl +``` + + +### 4.4 Service configuration and startup (Debian/Ubuntu) Make sure our tmpfiles configuration takes effect: @@ -292,7 +311,7 @@ sudo systemctl start zm-testagent ``` -### 4.4 Post-installation (Debian/Ubuntu) +### 4.5 Post-installation (Debian/Ubuntu) See the [post-installation] section for post-installation matters. @@ -347,7 +366,7 @@ 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. @@ -371,12 +390,10 @@ Configure Zonemaster::Backend to use the correct database path: sed -i '' '/[[:<:]]database_file[[:>:]]/ s:=.*:= /var/db/zonemaster/db.sqlite:' /usr/local/etc/zonemaster/backend_config.ini ``` -Create database directory and database with correct ownership: +Create database directory: ```sh install -v -m 755 -o zonemaster -g zonemaster -d /var/db/zonemaster -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -su -m zonemaster -c "perl create_db.pl" ``` > Some parameters can be changed, see the [backend configuration] documentation @@ -388,7 +405,17 @@ See appendices for [MariaDB][MariaDB instructions for FreeBSD] and [PostgreSQL][PostgreSQL instructions for FreeBSD]. -### 5.3 Service startup (FreeBSD) +### 5.3 Database configuration (FreeBSD) + +Finally initialize the database: + +```sh +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` +su -m zonemaster -c "perl create_db.pl" +``` + + +### 5.4 Service startup (FreeBSD) Enable services at startup: @@ -404,7 +431,7 @@ service zm_rpcapi start service zm_testagent start ``` -### 5.4 Post-installation (FreeBSD) +### 5.5 Post-installation (FreeBSD) To check the running daemons run: @@ -495,13 +522,6 @@ GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl -``` - #### A.2. MariaDB installation on Debian and Ubuntu @@ -539,13 +559,6 @@ GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl -``` - #### A.3. MySQL installation on FreeBSD @@ -605,13 +618,6 @@ exit; Then update the `/etc/zonemaster/backend_config.ini` file accordingly. -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -su -m zonemaster -c "perl create_db.pl" -``` - ### B. Installation with PostgreSQL @@ -665,13 +671,6 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl -``` - #### B.2. PostgreSQL installation on Debian and Ubuntu @@ -701,13 +700,6 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl -``` - #### B.3. PostgreSQL installation on FreeBSD @@ -739,13 +731,6 @@ This will ask for a password that should be copied in `backend_config.ini`. sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster ``` -Finally initialize the database: - -```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -su -m zonemaster -c "perl create_db.pl" -``` - ### C. Cleaning up the database From 9a75d7d943bb38111092d7d306f82b41518ef6c1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 15:47:54 +0200 Subject: [PATCH 238/424] Missing requirement --- lib/Zonemaster/Backend/DB.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index b0b935920..a71aa0e34 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -16,6 +16,7 @@ use Zonemaster::Engine::Profile requires qw( add_api_user_to_db add_batch_job + create_db create_new_batch_job create_new_test from_config From c90bd8494fbd3ec05a9dff36938be5edc13a9f8f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:21:16 +0200 Subject: [PATCH 239/424] Missing semi-colon --- lib/Zonemaster/Backend/DB.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index a71aa0e34..3398394d1 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -11,7 +11,7 @@ use Digest::MD5 qw(md5_hex); use Encode; use Log::Any qw( $log ); -use Zonemaster::Engine::Profile +use Zonemaster::Engine::Profile; requires qw( add_api_user_to_db From a1531b89ce9c873eaa05aed76ec1919512399ef1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 15:48:27 +0200 Subject: [PATCH 240/424] Make script executable --- share/create_db.pl | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 share/create_db.pl diff --git a/share/create_db.pl b/share/create_db.pl old mode 100644 new mode 100755 index 1f32b51cc..f49ff3e9c --- a/share/create_db.pl +++ b/share/create_db.pl @@ -1,3 +1,5 @@ +#!/usr/bin/env perl + use strict; use warnings; From a21d1b11b461a3fb9f5ad4551f830dda65387bbc Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:05:23 +0200 Subject: [PATCH 241/424] Oneline command --- docs/Installation.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 10687f34e..375f06a20 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -150,8 +150,7 @@ See appendices for [MariaDB][MariaDB instructions for CentOS] and Finally initialize the database: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl +sudo -u zonemaster perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` @@ -288,8 +287,7 @@ See appendices for [MariaDB][MariaDB instructions for Debian] and Finally initialize the database: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u zonemaster perl create_db.pl +sudo -u zonemaster perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` @@ -410,8 +408,7 @@ See appendices for [MariaDB][MariaDB instructions for FreeBSD] and Finally initialize the database: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -su -m zonemaster -c "perl create_db.pl" +su -m zonemaster -c "perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl" ``` From 9ef2cd3b452fb54c616b6ffdfac69e6b2914ce77 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:15:24 +0200 Subject: [PATCH 242/424] Explicitly logout from MariaDB --- docs/Installation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/Installation.md b/docs/Installation.md index 375f06a20..0c05f025c 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -517,6 +517,12 @@ CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; ``` +Logout from database: + +```sql +exit; +``` + Then update the `/etc/zonemaster/backend_config.ini` file accordingly. @@ -554,6 +560,12 @@ CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; ``` +Logout from database: + +```sql +exit; +``` + Then update the `/etc/zonemaster/backend_config.ini` file accordingly. From 4005158e5cd2a8573b4e2e73dd5f87f8764a1403 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:17:11 +0200 Subject: [PATCH 243/424] Improve wording --- docs/Installation.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 0c05f025c..425e6bcba 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -523,7 +523,8 @@ Logout from database: exit; ``` -Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Then update the `/etc/zonemaster/backend_config.ini` file with username and +password unless the default values are used. #### A.2. MariaDB installation on Debian and Ubuntu @@ -566,7 +567,8 @@ Logout from database: exit; ``` -Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Then update the `/etc/zonemaster/backend_config.ini` file with username and +password unless the default values are used. #### A.3. MySQL installation on FreeBSD @@ -625,7 +627,8 @@ Logout from database: exit; ``` -Then update the `/etc/zonemaster/backend_config.ini` file accordingly. +Then update the `/etc/zonemaster/backend_config.ini` file with username and +password unless the default values are used. ### B. Installation with PostgreSQL From 8b4c96c3f16b556098e8c8871b5e4878eb646ccb Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:17:22 +0200 Subject: [PATCH 244/424] Editorial update --- docs/Installation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 425e6bcba..782411ff3 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -529,12 +529,6 @@ password unless the default values are used. #### A.2. MariaDB installation on Debian and Ubuntu -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 @@ -543,6 +537,12 @@ sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini > **Note:** See the [backend configuration] documentation for details. +Install the database engine and its dependencies: + +```sh +sudo apt install mariadb-server libdbd-mysql-perl +``` + To initialize the database (unless you keep an old database) connect to the MariaDB server: From ebf641825b43c0af60b162c32059f192c026586b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 4 Aug 2021 09:37:34 +0200 Subject: [PATCH 245/424] add metrics --- lib/Zonemaster/Backend/Metrics.pm | 40 +++++++++++++++++++++++++++ script/zonemaster_backend_rpcapi.psgi | 8 +++++- script/zonemaster_db_exporter.psgi | 35 +++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 lib/Zonemaster/Backend/Metrics.pm create mode 100644 script/zonemaster_db_exporter.psgi diff --git a/lib/Zonemaster/Backend/Metrics.pm b/lib/Zonemaster/Backend/Metrics.pm new file mode 100644 index 000000000..392338a2b --- /dev/null +++ b/lib/Zonemaster/Backend/Metrics.pm @@ -0,0 +1,40 @@ +package Zonemaster::Backend::Metrics; + +eval("use Net::Statsd"); + +my $enable_metrics = 0; + +if (!$@ && defined $ENV{ZM_STATS_HOST}) { + $enable_metrics = 1; + $Net::Statsd::HOST = $ENV{ZM_STATS_HOST}; + $Net::Statsd::PORT = $ENV{ZM_STATS_PORT} // 8125; +} + +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 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(@_) + } +} diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index bae6fdf50..4955da9d1 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -23,6 +23,7 @@ BEGIN { $ENV{PERL_JSON_BACKEND} = 'JSON::PP' }; use Zonemaster::Backend::RPCAPI; use Zonemaster::Backend::Config; +use Zonemaster::Backend::Metrics; local $| = 1; @@ -148,6 +149,8 @@ my $dispatch = JSON::RPC::Dispatch->new( router => $router, ); +use Data::Dumper; + sub { my $env = shift; my $req = Plack::Request->new($env); @@ -169,7 +172,10 @@ sub { $res->body( encode_json($errors) ); $res->finalize; } else { - $dispatch->handle_psgi($env, $env->{REMOTE_HOST}); + $res = $dispatch->handle_psgi($env, $env->{REMOTE_HOST}); + 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_db_exporter.psgi b/script/zonemaster_db_exporter.psgi new file mode 100644 index 000000000..bb7144a2d --- /dev/null +++ b/script/zonemaster_db_exporter.psgi @@ -0,0 +1,35 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use Zonemaster::Backend::Config; +use Zonemaster::Backend::DB; +use Net::Prometheus; + +my $client = Net::Prometheus->new; + +my $tests_gauge = $client->new_gauge( + name => 'zonemaster_tests_total', + help => 'Total number of tests', + labels => [ 'state' ], +); + +my $config = Zonemaster::Backend::Config->load_config(); +my $dbtype = $config->DB_engine; +my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); +my $db = $dbclass->from_config( $config ); + +my $prom_app = $client->psgi_app; + +sub { + my $queued = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 0'); + $tests_gauge->labels('queued')->set($queued->{count}); + + my $finished = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 100'); + $tests_gauge->labels('finished')->set($finished->{count}); + + my $running = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress > 0 and progress < 100'); + $tests_gauge->labels('running')->set($running->{count}); + + $prom_app->(@_); +}; From 462e54b961ddc72d5167ea69f36013b4fadd3c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 11 Aug 2021 16:22:32 +0200 Subject: [PATCH 246/424] add test agent metrics --- script/zonemaster_backend_testagent | 5 +++++ script/zonemaster_db_exporter.psgi | 35 ----------------------------- 2 files changed, 5 insertions(+), 35 deletions(-) delete mode 100644 script/zonemaster_db_exporter.psgi diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index eab4507a7..22fd103d8 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; @@ -136,6 +137,7 @@ sub main { while ( !$caught_sigterm ) { $self->pm->reap_finished_children(); # Reaps terminated child processes $self->pm->on_wait(); # Sends SIGKILL to overdue child processes + Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.running_process", scalar($self->pm->running_procs)); my $id = $self->db->get_test_request( $self->config->ZONEMASTER_lock_on_queue ); $self->db->process_unfinished_tests( @@ -147,13 +149,16 @@ sub main { $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"); 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, $self->config->ZONEMASTER_maximal_number_of_retries ) } else { + Zonemaster::Backend::Metrics::increment("zonemaster.testagent.tests_completed"); $log->info( "Test completed: $id" ); } $agent->reset(); diff --git a/script/zonemaster_db_exporter.psgi b/script/zonemaster_db_exporter.psgi deleted file mode 100644 index bb7144a2d..000000000 --- a/script/zonemaster_db_exporter.psgi +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use Zonemaster::Backend::Config; -use Zonemaster::Backend::DB; -use Net::Prometheus; - -my $client = Net::Prometheus->new; - -my $tests_gauge = $client->new_gauge( - name => 'zonemaster_tests_total', - help => 'Total number of tests', - labels => [ 'state' ], -); - -my $config = Zonemaster::Backend::Config->load_config(); -my $dbtype = $config->DB_engine; -my $dbclass = Zonemaster::Backend::DB->get_db_class( $dbtype ); -my $db = $dbclass->from_config( $config ); - -my $prom_app = $client->psgi_app; - -sub { - my $queued = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 0'); - $tests_gauge->labels('queued')->set($queued->{count}); - - my $finished = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress = 100'); - $tests_gauge->labels('finished')->set($finished->{count}); - - my $running = $db->dbh->selectrow_hashref('SELECT count(*) from test_results WHERE progress > 0 and progress < 100'); - $tests_gauge->labels('running')->set($running->{count}); - - $prom_app->(@_); -}; From b340f241efa57799e907ec1b1d37f22259edb9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 11 Aug 2021 16:34:06 +0200 Subject: [PATCH 247/424] add max procs metric --- script/zonemaster_backend_testagent | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index 22fd103d8..6133c5d31 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -133,11 +133,12 @@ sub main { local $SIG{TERM} = $catch_sigterm; my $agent = Zonemaster::Backend::TestAgent->new( { config => $self->config } ); + Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.maximum_processes", $self->pm->max_procs); while ( !$caught_sigterm ) { $self->pm->reap_finished_children(); # Reaps terminated child processes $self->pm->on_wait(); # Sends SIGKILL to overdue child processes - Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.running_process", scalar($self->pm->running_procs)); + Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.running_processes", scalar($self->pm->running_procs)); my $id = $self->db->get_test_request( $self->config->ZONEMASTER_lock_on_queue ); $self->db->process_unfinished_tests( From 356b89a8bea2002f7aae89cbeb171c2dd0ffc2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 16:43:25 +0200 Subject: [PATCH 248/424] add test duration metrics --- lib/Zonemaster/Backend/Metrics.pm | 6 ++++++ script/zonemaster_backend_testagent | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Metrics.pm b/lib/Zonemaster/Backend/Metrics.pm index 392338a2b..89a3444b2 100644 --- a/lib/Zonemaster/Backend/Metrics.pm +++ b/lib/Zonemaster/Backend/Metrics.pm @@ -38,3 +38,9 @@ sub gauge { Net::Statsd::gauge(@_) } } + +sub timing { + if ( $enable_metrics ) { + Net::Statsd::timing(@_) + } +} diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index 6133c5d31..e2ed47228 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -17,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); ### @@ -151,6 +151,7 @@ sub main { 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 $@; @@ -162,6 +163,7 @@ sub main { 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 } From ca280268ed525e451e79b16bafb4ee0208a32fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 17:01:45 +0200 Subject: [PATCH 249/424] update installation doc --- docs/Installation.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/Installation.md b/docs/Installation.md index f4b1bafb9..07e9efcc4 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -75,6 +75,13 @@ Install dependencies not available from binary packages: sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman ``` +Install optional dependencies: + +```sh +# For metrics feature +sudo cpanm Net::Statsd +``` + Install Zonemaster::Backend: ```sh @@ -207,6 +214,13 @@ Install dependencies not available from binary packages: sudo cpanm Daemon::Control JSON::Validator ``` +Install optional dependencies: + +```sh +# For metrics feature +sudo cpanm Net::Statsd +``` + Install Zonemaster::Backend: ```sh @@ -319,6 +333,12 @@ pkg install jq p5-Class-Method-Modifiers p5-Config-IniFiles p5-Daemon-Control p5 ``` +Install optional dependencies: + +```sh +# For metrics feature +sudo cpanm Net::Statsd +``` Install Zonemaster::Backend: From 1fa1c9d9c485171ecd71e1efd22e3fca3371cc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 24 Aug 2021 14:18:12 +0200 Subject: [PATCH 250/424] add configuration section for metrics --- docs/Configuration.md | 31 +++++++++++++++++++++++++++ lib/Zonemaster/Backend/Config.pm | 14 +++++++++++- lib/Zonemaster/Backend/Metrics.pm | 17 ++++++++++++--- script/zonemaster_backend_rpcapi.psgi | 2 ++ script/zonemaster_backend_testagent | 5 ++++- 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 4a97d3829..177eb7f6a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -26,6 +26,9 @@ * [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) @@ -289,6 +292,34 @@ 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`. + +### Available metrics + +| Name | Type | +| --- | --- | +| zonemaster.rpcapi.requests.\.\ | Counter | +| zonemaster.testagent.tests_started | Counter | +| zonemaster.testagent.tests_completed | Counter | +| zonemaster.testagent.tests_died | Counter | +| zonemaster.testagent.tests_duration_seconds | Timing | +| zonemaster.testagent.running_processes | Gauge | +| zonemaster.testagent.maximum_processes | Gauge | ## PUBLIC PROFILES and PRIVATE PROFILES sections diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 8165dfa62..42579e61f 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -127,7 +127,7 @@ sub parse { # Validate section names { - my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER' ); + my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'METRICS' ); for my $section ( $ini->Sections ) { if ( !exists $sections{$section} ) { die "config: unrecognized section: $section\n"; @@ -147,6 +147,8 @@ sub parse { $obj->_set_ZONEMASTER_age_reuse_previous_test( '600' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); + #$obj->_set_METRICS_statsd_host( undef ); + $obj->_set_METRICS_statsd_port( '8125' ); # Assign property values (part 1/2) if ( defined( my $value = $get_and_clear->( 'DB', 'engine' ) ) ) { @@ -275,6 +277,12 @@ 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->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { $obj->_reset_LANGUAGE_locale(); @@ -589,6 +597,8 @@ sub ZONEMASTER_lock_on_queue { return $_[0]->{_ZONEMA 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}; } # Compile time generation of setters for the properties documented above UNITCHECK { @@ -610,6 +620,8 @@ UNITCHECK { _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 ); } =head2 new_DB diff --git a/lib/Zonemaster/Backend/Metrics.pm b/lib/Zonemaster/Backend/Metrics.pm index 89a3444b2..ba84045a9 100644 --- a/lib/Zonemaster/Backend/Metrics.pm +++ b/lib/Zonemaster/Backend/Metrics.pm @@ -1,13 +1,13 @@ package Zonemaster::Backend::Metrics; +use Log::Any qw($log); + eval("use Net::Statsd"); my $enable_metrics = 0; -if (!$@ && defined $ENV{ZM_STATS_HOST}) { +if (!$@) { $enable_metrics = 1; - $Net::Statsd::HOST = $ENV{ZM_STATS_HOST}; - $Net::Statsd::PORT = $ENV{ZM_STATS_PORT} // 8125; } my %CODE_STATUS_HASH = ( @@ -18,6 +18,17 @@ my %CODE_STATUS_HASH = ( -32603 => 'RPC_INTERNAL_ERROR' ); +sub setup { + my ( $cls, $host, $port ) = @_; + if (!defined $host) { + $enable_metrics = 0; + } else { + $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) { diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 4955da9d1..5175a43b2 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -62,6 +62,8 @@ $SIG{__WARN__} = sub { 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; diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index e2ed47228..cc44b329a 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -133,11 +133,12 @@ sub main { local $SIG{TERM} = $catch_sigterm; my $agent = Zonemaster::Backend::TestAgent->new( { config => $self->config } ); - Zonemaster::Backend::Metrics::gauge("zonemaster.testagent.maximum_processes", $self->pm->max_procs); while ( !$caught_sigterm ) { $self->pm->reap_finished_children(); # Reaps terminated child processes $self->pm->on_wait(); # Sends SIGKILL to overdue child processes + + 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)); my $id = $self->db->get_test_request( $self->config->ZONEMASTER_lock_on_queue ); @@ -199,6 +200,8 @@ eval { $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 } ); From 884a4d31c3deccbf4ee0438b9a80ee13e65fad79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 24 Aug 2021 14:20:59 +0200 Subject: [PATCH 251/424] update default configuration --- share/backend_config.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/backend_config.ini b/share/backend_config.ini index daf586ec9..9bda13598 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -40,3 +40,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 From 79e0ef5e26d953e916874be43e9dbbf7c042fbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 16:03:25 +0200 Subject: [PATCH 252/424] improve doc formatting --- docs/Configuration.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 177eb7f6a..3386ae43c 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -311,15 +311,15 @@ Default value: `8125`. ### Available metrics -| Name | Type | -| --- | --- | +| Name | Type | +| ---------------------------------------------- | ------- | | zonemaster.rpcapi.requests.\.\ | Counter | -| zonemaster.testagent.tests_started | Counter | -| zonemaster.testagent.tests_completed | Counter | -| zonemaster.testagent.tests_died | Counter | -| zonemaster.testagent.tests_duration_seconds | Timing | -| zonemaster.testagent.running_processes | Gauge | -| zonemaster.testagent.maximum_processes | Gauge | +| zonemaster.testagent.tests_started | Counter | +| zonemaster.testagent.tests_completed | Counter | +| zonemaster.testagent.tests_died | Counter | +| zonemaster.testagent.tests_duration_seconds | Timing | +| zonemaster.testagent.running_processes | Gauge | +| zonemaster.testagent.maximum_processes | Gauge | ## PUBLIC PROFILES and PRIVATE PROFILES sections From 5104cbaa886c1c1cd33117edda21e788ab63b1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 16:06:24 +0200 Subject: [PATCH 253/424] add net::statsd to recommended modules --- Makefile.PL | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.PL b/Makefile.PL index 287e35110..28a1b73f2 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -41,6 +41,7 @@ test_requires 'Test::NoWarnings'; recommends 'DBD::mysql'; recommends 'DBD::Pg'; recommends 'DBD::SQLite'; +recommends 'Net::Statsd'; install_share; From b48f7df9bea5ddf64f0ee0b2f18b62f4639ec081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 16:07:04 +0200 Subject: [PATCH 254/424] remove debug --- script/zonemaster_backend_rpcapi.psgi | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 5175a43b2..69d50f019 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -151,8 +151,6 @@ my $dispatch = JSON::RPC::Dispatch->new( router => $router, ); -use Data::Dumper; - sub { my $env = shift; my $req = Plack::Request->new($env); From b2b0625772ae937be5b369666fa467b42666a3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 8 Sep 2021 15:42:57 +0200 Subject: [PATCH 255/424] update makefile.pl --- Makefile.PL | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile.PL b/Makefile.PL index 28a1b73f2..287e35110 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -41,7 +41,6 @@ test_requires 'Test::NoWarnings'; recommends 'DBD::mysql'; recommends 'DBD::Pg'; recommends 'DBD::SQLite'; -recommends 'Net::Statsd'; install_share; From 74f21cc2a1949379e4b5215f33443fcf6a933940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 8 Sep 2021 16:21:54 +0200 Subject: [PATCH 256/424] update documentation --- docs/Installation.md | 47 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 07e9efcc4..34d6f5091 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -75,13 +75,6 @@ Install dependencies not available from binary packages: sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman ``` -Install optional dependencies: - -```sh -# For metrics feature -sudo cpanm Net::Statsd -``` - Install Zonemaster::Backend: ```sh @@ -214,13 +207,6 @@ Install dependencies not available from binary packages: sudo cpanm Daemon::Control JSON::Validator ``` -Install optional dependencies: - -```sh -# For metrics feature -sudo cpanm Net::Statsd -``` - Install Zonemaster::Backend: ```sh @@ -333,13 +319,6 @@ pkg install jq p5-Class-Method-Modifiers p5-Config-IniFiles p5-Daemon-Control p5 ``` -Install optional dependencies: - -```sh -# For metrics feature -sudo cpanm Net::Statsd -``` - Install Zonemaster::Backend: ```sh @@ -723,6 +702,32 @@ sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! Remove the database file and recreate it following the installation instructions above. +## D. Optional features + +### D.1 Metrics + +Statsd metrics are available, to enable the feature install the additional `Net::Statsd` module. See the [configuration][Backend configuration] for additional information. + +### D.1.1 Installation on Centos + +```sh +sudo cpanm Net::Statsd +``` + +### D.1.2 Installation on Debian / Ubuntu + + +```sh +sudo apt install libnet-statsd-perl +``` + +### D.1.3 Installation on Freebsd + +```sh +# For metrics feature +cpanm Net::Statsd +``` + ------- [Backend configuration]: Configuration.md From 3d74caa6dbe434784a24240f8c39a39324a2a340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 8 Sep 2021 16:24:47 +0200 Subject: [PATCH 257/424] remove unused line --- lib/Zonemaster/Backend/Config.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 42579e61f..8439b7948 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -147,7 +147,6 @@ sub parse { $obj->_set_ZONEMASTER_age_reuse_previous_test( '600' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); - #$obj->_set_METRICS_statsd_host( undef ); $obj->_set_METRICS_statsd_port( '8125' ); # Assign property values (part 1/2) From 833aa73616b532093b7d5295be29289ac9a7f155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 9 Sep 2021 11:36:59 +0200 Subject: [PATCH 258/424] update doc --- docs/Configuration.md | 11 ----------- docs/Installation.md | 9 ++++++++- docs/Telemetry.md | 18 ++++++++++++++++++ share/backend_config.ini | 2 +- 4 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 docs/Telemetry.md diff --git a/docs/Configuration.md b/docs/Configuration.md index 3386ae43c..4d3d9b1da 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -309,17 +309,6 @@ Note that this feature requires the `Net::Statsd` module to be installed. The port the StatsD receiver is listening on. Default value: `8125`. -### Available metrics - -| Name | Type | -| ---------------------------------------------- | ------- | -| zonemaster.rpcapi.requests.\.\ | Counter | -| zonemaster.testagent.tests_started | Counter | -| zonemaster.testagent.tests_completed | Counter | -| zonemaster.testagent.tests_died | Counter | -| zonemaster.testagent.tests_duration_seconds | Timing | -| zonemaster.testagent.running_processes | Gauge | -| zonemaster.testagent.maximum_processes | Gauge | ## PUBLIC PROFILES and PRIVATE PROFILES sections diff --git a/docs/Installation.md b/docs/Installation.md index 34d6f5091..8fd04de55 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -27,6 +27,8 @@ * [A. Installation with MariaDB](#a-installation-with-mariadb) * [B. Installation with PostgreSQL](#b-installation-with-postgresql) * [C. Cleaning up the database](#c-cleaning-up-the-database) + * [D. Optional features](#d-optional-features) + * [D.1 Metrics](#d1-metrics) ## 1. Overview @@ -706,7 +708,11 @@ Remove the database file and recreate it following the installation instructions ### D.1 Metrics -Statsd metrics are available, to enable the feature install the additional `Net::Statsd` module. See the [configuration][Backend configuration] for additional information. +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]; ### D.1.1 Installation on Centos @@ -749,3 +755,4 @@ cpanm Net::Statsd [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 +[metrics]: Telemetry.md#metrics diff --git a/docs/Telemetry.md b/docs/Telemetry.md new file mode 100644 index 000000000..6b2e6070f --- /dev/null +++ b/docs/Telemetry.md @@ -0,0 +1,18 @@ +# Telemetry + +## Metrics + +If [enabled][metrics feature], Statsd compatible metrics are available to use: + +| Name | Type | +| ---------------------------------------------- | ------- | +| zonemaster.rpcapi.requests.\.\ | Counter | +| zonemaster.testagent.tests_started | Counter | +| zonemaster.testagent.tests_completed | Counter | +| zonemaster.testagent.tests_died | Counter | +| zonemaster.testagent.tests_duration_seconds | Timing | +| zonemaster.testagent.running_processes | Gauge | +| zonemaster.testagent.maximum_processes | Gauge | + + +[metrics feature]: Installation.md#d1-metrics diff --git a/share/backend_config.ini b/share/backend_config.ini index 9bda13598..1a287dd93 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -44,4 +44,4 @@ locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE [METRICS] # Uncoment the following option to enable the metrics feature #statsd_host = localhost -statsd_port = 8125 +#statsd_port = 8125 From 353ee36f59f15f963084fb085327740092407221 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 13 Sep 2021 16:53:06 +0200 Subject: [PATCH 259/424] Fix links --- docs/Installation.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 782411ff3..1418a49a0 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -141,8 +141,8 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster #### 3.2.2 Instructions for other engines (CentOS) -See appendices for [MariaDB][MariaDB instructions for CentOS] and -[PostgreSQL][PostgreSQL instructions for CentOS]. +See appendices for [MariaDB][MariaDB instructions on CentOS] and +[PostgreSQL][PostgreSQL instructions on CentOS]. ### 3.3 Database configuration (CentOS) @@ -278,8 +278,8 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster #### 4.2.2 Instructions for other engines (Debian/Ubuntu) -See appendices for [MariaDB][MariaDB instructions for Debian] and -[PostgreSQL][PostgreSQL instructions for Debian]. +See appendices for [MariaDB][MariaDB instructions on Debian] and +[PostgreSQL][PostgreSQL instructions on Debian]. ### 4.3 Database configuration (Debian/Ubuntu) @@ -399,8 +399,8 @@ install -v -m 755 -o zonemaster -g zonemaster -d /var/db/zonemaster #### 5.2.2 Instructions for other engines (FreeBSD) -See appendices for [MariaDB][MariaDB instructions for FreeBSD] and -[PostgreSQL][PostgreSQL instructions for FreeBSD]. +See appendices for [MariaDB][MariaDB instructions on FreeBSD] and +[PostgreSQL][PostgreSQL instructions on FreeBSD]. ### 5.3 Database configuration (FreeBSD) @@ -777,12 +777,12 @@ Remove the database file and recreate it following the installation instructions [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 for CentOS]: #a1-mariadb-installation-for-centos -[MariaDB instructions for Debian]: #a2-mariadb-installation-for-debian-and-ubuntu -[MariaDB instructions for FreeBSD]: #a3-mysql-installation-for-freebsd -[PostgreSQL instructions for CentOS]: #b1-postgresql-installation-for-centos -[PostgreSQL instructions for Debian]: #b2-postgresql-installation-for-debian-and-ubuntu -[PostgreSQL instructions for FreeBSD]: #b3-postgresql-installation-for-freebsd +[MariaDB instructions on CentOS]: #a1-mariadb-installation-on-centos +[MariaDB instructions on Debian]: #a2-mariadb-installation-on-debian-and-ubuntu +[MariaDB instructions on FreeBSD]: #a3-mysql-installation-on-freebsd +[PostgreSQL instructions on CentOS]: #b1-postgresql-installation-on-centos +[PostgreSQL instructions on Debian]: #b2-postgresql-installation-on-debian-and-ubuntu +[PostgreSQL instructions on FreeBSD]: #b3-postgresql-installation-on-freebsd [Post-installation]: #6-post-installation [README.md-upgrade]: /README.md#upgrade [Upgrade database]: #7-upgrade-zonemaster-database From 11c01c44f94156a03ee91d05b85e736e37428b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 14 Sep 2021 14:03:05 +0200 Subject: [PATCH 260/424] fix log message --- lib/Zonemaster/Backend/Metrics.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/Metrics.pm b/lib/Zonemaster/Backend/Metrics.pm index ba84045a9..e2b40310e 100644 --- a/lib/Zonemaster/Backend/Metrics.pm +++ b/lib/Zonemaster/Backend/Metrics.pm @@ -22,7 +22,7 @@ sub setup { my ( $cls, $host, $port ) = @_; if (!defined $host) { $enable_metrics = 0; - } else { + } elsif ( $enable_metrics ) { $log->info('Enabling metrics module', { host => $host, port => $port }); $Net::Statsd::HOST = $host; $Net::Statsd::PORT = $port; From 34fca0844017b1a39aecde5580ce48d8acaee181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 14 Sep 2021 14:06:05 +0200 Subject: [PATCH 261/424] update doc --- docs/Installation.md | 1 - docs/Telemetry.md | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 8fd04de55..61c029070 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -730,7 +730,6 @@ sudo apt install libnet-statsd-perl ### D.1.3 Installation on Freebsd ```sh -# For metrics feature cpanm Net::Statsd ``` diff --git a/docs/Telemetry.md b/docs/Telemetry.md index 6b2e6070f..ac8c747ab 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -2,7 +2,7 @@ ## Metrics -If [enabled][metrics feature], Statsd compatible metrics are available to use: +If [enabled][metrics feature], [Statsd][statsd] compatible metrics are available to use: | Name | Type | | ---------------------------------------------- | ------- | @@ -16,3 +16,4 @@ If [enabled][metrics feature], Statsd compatible metrics are available to use: [metrics feature]: Installation.md#d1-metrics +[statsd]: https://github.com/statsd/statsd From adfd44ecd62f9985d425995aa3ac7944f0287e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 14 Sep 2021 14:07:19 +0200 Subject: [PATCH 262/424] update manifest --- MANIFEST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST b/MANIFEST index 7088656d2..f31843136 100644 --- a/MANIFEST +++ b/MANIFEST @@ -6,6 +6,7 @@ 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 @@ -30,6 +31,7 @@ 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 From 98d79c23484587049856d8d7742fdabafe590b6c Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 14 Sep 2021 20:33:06 +0200 Subject: [PATCH 263/424] Fix calling create_db command --- docs/Installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 1418a49a0..b9d95cd0a 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -150,7 +150,7 @@ See appendices for [MariaDB][MariaDB instructions on CentOS] and Finally initialize the database: ```sh -sudo -u zonemaster perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl +sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` @@ -287,7 +287,7 @@ See appendices for [MariaDB][MariaDB instructions on Debian] and Finally initialize the database: ```sh -sudo -u zonemaster perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl +sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl ``` @@ -408,7 +408,7 @@ See appendices for [MariaDB][MariaDB instructions on FreeBSD] and Finally initialize the database: ```sh -su -m zonemaster -c "perl $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl" +su -m zonemaster -c "`perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir(qw(Zonemaster-Backend))'`/create_db.pl" ``` From 27ef8269c7d897d33ff0d7bae7141c8686c67f2b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 5 Aug 2021 16:35:57 +0200 Subject: [PATCH 264/424] Missing indexes on database creation (SQLite) --- lib/Zonemaster/Backend/DB/SQLite.pm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 5e14d3400..1462ab097 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -78,6 +78,23 @@ sub create_db { ' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + $self->dbh->do( + 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' + ); + $self->dbh->do( + 'CREATE INDEX test_results__fingerprint ON test_results (params_deterministic_hash)' + ); + $self->dbh->do( + 'CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)' + ); + $self->dbh->do( + 'CREATE INDEX test_results__progress ON test_results (progress)' + ); + $self->dbh->do( + 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' + ); + + #################################################################### # BATCH JOBS #################################################################### @@ -90,6 +107,7 @@ sub create_db { ' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + #################################################################### # USERS #################################################################### From b6120311367721b84d251f0a46ad80c0b2b0bcab Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Sat, 18 Sep 2021 18:01:19 +0200 Subject: [PATCH 265/424] Updates to v2021.1.3 release --- Changes | 32 +++++++++++++++++++++++++------- lib/Zonemaster/Backend.pm | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Changes b/Changes index 36e0e40fa..5750a76e5 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,23 @@ Release history for Zonemaster component Zonemaster-Backend +v7.0.0 2021-09-15 (public security release) + [Fixes] + - By design adding a API user (needed for the batch function) is limited to + connections over localhost. With a default GUI installation with reverse + proxy all connections are over localhost, which means that adding a API + user is publicly available if the GUI is publicly available. If you can + add API users, then you can start several large batch jobs which may + overload the Zonemaster system. (#838, #850) + - Makes RPCAPI use the real remote IP for verification to restore the + limitation that the API key can only be added from localhost. + - Disables RPCAPI method "add_api_user" by default. + - Adds configuration key for "backend_config.ini" to enable RPCAPI method + "add_api_user". + - Adds configuration key for "backend_config.ini" to disnable RPCAPI method + "add_batch_job". + - Prevents RPCAPI daemon to recreate workers when workers crashed to do error + in configuration file. This is a FreeBSD specific error. (#813, #862) + v6.2.0 2021-05-28 (public release version) [Features] @@ -204,7 +222,7 @@ v3.0.0 2019-01-27 (pre-release version) * All link references on Github now to zonemaster/zonemaster instead of old dotse/zonemaster #443 * Added missing support for "filter" in "get_test_history" #446 - * Add a note about the empty string, stating that it's + * Add a note about the empty string, stating that it's allowed but deprecated. #413 * Fixed invalid Zonemaster::Backend::Config call #472 * Remove geolocation code #462 @@ -249,14 +267,14 @@ v2.0.2 2018-02-23 v2.0.1 2018-01-12 Natural Language support * Update Translator.pm Add support for Danish "da" in Backend. (#346) - + Fixed * Workaround for "query of death" problem (#287, #325) * Partial fix of leakage of system path information (#334) * Fixed the issue that validate_syntax and start_domain_test functions do not correctly validate IPv4 and IPv6 addresses (#173, #328) * Fixed: Use of uninitialized value $config/$policy (#268, #329) - * Fixed incomplete stored data for unit test and fixed bug in + * Fixed incomplete stored data for unit test and fixed bug in TestAgent.pm (#337, #342) * Fix config and start files (#336) * Fixed: ipv4 || ipv4 must be ipv4 || ipv6 (#319, #326) @@ -327,10 +345,10 @@ v1.0.6 2016-10-11 Fixes #155 - Change de preflight test to block only on Basic00 Fixes #153 - Improve the batch API (Fixed and added bulk testing methods) v1.0.5 2015-12-17 - Fixes #148 - Use iana_profile.josn instead of iana.json as source file for IANA tests + Fixes #148 - Use iana_profile.josn instead of iana.json as source file for IANA tests Fixes #141 - Database initialisation files (.sql) not updated with the new hash_id column Fixes #138 - The Bacakend's generated JSON is locale dependant - Fixes #134 - Bug fix of the crontab job runner + Fixes #134 - Bug fix of the crontab job runner Fixes #127 - The Bakend Translator does not handle non scalar message parameters Fixes #125 - Non numeric IDs for tests Fixes #124 - modified all instances of .SE to IIS @@ -355,7 +373,7 @@ v1.0.2 2015-05-11 Fixes #99 - Fixes #59 Fixes #98 - Further updates for Debian instructions 2 Fixes #97 - Debian instructions for the backend updated - Fixes #96 - Debian-compatible start script + Fixes #96 - Debian-compatible start script Fixes #93 - Make test more robust Fixes #92 - Updates backend install for Debian Fixes #91 - API documentation needs to be improved @@ -427,7 +445,7 @@ v1.0.0 2014-12-11 Public beta release. Fixes #129 - History should differentiate from delegated, undelegated and batch Fixes #152 - Delay in start of the test (when the same page is used for testing a second domain) Fixes #121 - Does not support Swedish language - Fixes #132 - Does not run for all broken domains (e.g. broken.dnssec.ee) + Fixes #132 - Does not run for all broken domains (e.g. broken.dnssec.ee) Fixes #139 - No line-feed in output from GUI Fixes #127 - Does not support IDN 2.0 domains Fixes #117 - Disable both IPv4 and IPv6 possible diff --git a/lib/Zonemaster/Backend.pm b/lib/Zonemaster/Backend.pm index 5c430690f..12f18ba38 100644 --- a/lib/Zonemaster/Backend.pm +++ b/lib/Zonemaster/Backend.pm @@ -1,6 +1,6 @@ package Zonemaster::Backend; -our $VERSION = '6.2.0'; +our $VERSION = '7.0.0'; use strict; use warnings; From e233c614570a5e33bb32b82ee9cdbf499d350d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 20 Sep 2021 08:44:58 +0200 Subject: [PATCH 266/424] update doc --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 287be8aa5..b11302e58 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1000,7 +1000,7 @@ An object with the following properties: `"ERROR"`, but none with `"CRITICAL"`. * `"critical"`, if there is at least one message with *severity level* `"CRITICAL"`. -* `"undelegated"`: `1` if the test is undelegated, `0` otherwise. +* `"undelegated"`: `true` if the test is undelegated, `false` otherwise. #### `"error"` From 9b8549b95043cb987f882dc904a426cfb50a216f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 20 Sep 2021 09:11:20 +0200 Subject: [PATCH 267/424] update documentation --- docs/Telemetry.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/Telemetry.md b/docs/Telemetry.md index ac8c747ab..b2a23406c 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -4,16 +4,32 @@ If [enabled][metrics feature], [Statsd][statsd] compatible metrics are available to use: -| Name | Type | -| ---------------------------------------------- | ------- | -| zonemaster.rpcapi.requests.\.\ | Counter | -| zonemaster.testagent.tests_started | Counter | -| zonemaster.testagent.tests_completed | Counter | -| zonemaster.testagent.tests_died | Counter | -| zonemaster.testagent.tests_duration_seconds | Timing | -| zonemaster.testagent.running_processes | Gauge | -| zonemaster.testagent.maximum_processes | Gauge | +| 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.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 -lu 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. + [metrics feature]: Installation.md#d1-metrics [statsd]: https://github.com/statsd/statsd +[StatsD exporter]: https://github.com/grafana/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 From d7eba683eb5c758a9a4e4671d856d1b2fb9d8f58 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 20 Sep 2021 10:17:44 +0200 Subject: [PATCH 268/424] Empty From d9ad37b756de3a9db201c8f71d2cb28ba942ffab Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 20 Sep 2021 17:02:02 +0200 Subject: [PATCH 269/424] Change link from http to https --- docs/API.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index f872327e7..b02aa9031 100644 --- a/docs/API.md +++ b/docs/API.md @@ -54,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 @@ -1369,6 +1369,7 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [Delegation Signer]: https://datatracker.ietf.org/doc/html/rfc4034#section-5 [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 +[JSON-RPC 2.0]: https://www.jsonrpc.org/specification [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag [Validation error data]: #validation-error-data From 093bc61c3fc6e47652cfe4fa0d14139feb4767b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 5 Aug 2021 13:20:42 +0200 Subject: [PATCH 270/424] use plack reverse proxy middleware to get real remote ip --- docs/Installation.md | 6 +++--- script/zonemaster_backend_rpcapi.psgi | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index f4b1bafb9..c36195900 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -72,7 +72,7 @@ sudo yum -y install jq perl-Class-Method-Modifiers perl-Config-IniFiles perl-DBD Install dependencies not available from binary packages: ```sh -sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman +sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman Plack::Middleware::ReverseProxy ``` Install Zonemaster::Backend: @@ -195,7 +195,7 @@ 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 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 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 ``` > **Note**: libio-stringy-perl is listed here even though it's not a direct @@ -315,7 +315,7 @@ 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-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-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 ``` diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index bae6fdf50..9aba9d6c9 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -148,7 +148,7 @@ my $dispatch = JSON::RPC::Dispatch->new( router => $router, ); -sub { +my $rpcapi_app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = {}; @@ -169,7 +169,7 @@ sub { $res->body( encode_json($errors) ); $res->finalize; } else { - $dispatch->handle_psgi($env, $env->{REMOTE_HOST}); + $dispatch->handle_psgi($env, $env->{REMOTE_ADDR}); } } else { $res = Plack::Response->new(200); @@ -186,3 +186,8 @@ sub { } }; + +builder { + enable "Plack::Middleware::ReverseProxy"; + mount "/" => $rpcapi_app; +}; From 0acdb7f2403a655420953763ae28166f08ed530b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 10 Aug 2021 13:34:09 +0200 Subject: [PATCH 271/424] add config key to disable batch / user api --- docs/Configuration.md | 17 ++++++++++++++ lib/Zonemaster/Backend/Config.pm | 32 ++++++++++++++++++++++++++- lib/Zonemaster/Backend/Validator.pm | 12 ++++++++++ script/zonemaster_backend_rpcapi.psgi | 22 ++++++++++-------- share/backend_config.ini | 4 ++++ 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 4a97d3829..d871abd3a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -51,6 +51,23 @@ Repeating a key name in one section is forbidden. Each section in `backend_config.ini` is documented below. +## API section + +Available keys: `enable_batch_jobs`, `enable_add_api_user`. + +### enable_batch_jobs + +Boolean value to enable the batch jobs methods of the API. + +Accpected values: 0 or 1, default to 0 (disabled). + +### enable_add_api_user + +Boolean value to enable the `add_api_user` method of the API. + +Accpected values: 0 or 1, default to 0 (disabled). + + ## DB section Available keys : `engine`, `user`, `password`, `database_name`, diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 8165dfa62..4f78a16ce 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -127,7 +127,7 @@ sub parse { # Validate section names { - my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER' ); + my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'API'); for my $section ( $ini->Sections ) { if ( !exists $sections{$section} ) { die "config: unrecognized section: $section\n"; @@ -145,6 +145,8 @@ sub parse { $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_API_enable_add_api_user( '0' ); + $obj->_set_API_enable_batch_jobs( '0' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); @@ -275,6 +277,12 @@ 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->( 'API', 'enable_add_api_user' ) ) ) { + $obj->_set_API_enable_add_api_user( $value ); + } + if ( defined( my $value = $get_and_clear->( 'API', 'enable_batch_jobs' ) ) ) { + $obj->_set_API_enable_batch_jobs( $value ); + } if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { $obj->_reset_LANGUAGE_locale(); @@ -567,6 +575,24 @@ Returns a number. =cut +=head2 API_enable_add_api_user + +Get the value of +L. + +Return 0 or 1 + +=cut + +=head2 API_enable_batch_jobs + +Get the value of +L. + +Return 0 or 1 + +=cut + # Getters for the properties documented above sub DB_polling_interval { return $_[0]->{_DB_polling_interval}; } sub MYSQL_host { return $_[0]->{_MYSQL_host}; } @@ -589,6 +615,8 @@ sub ZONEMASTER_lock_on_queue { return $_[0]->{_ZONEMA 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 API_enable_add_api_user { return $_[0]->{_API_enable_add_api_user}; } +sub API_enable_batch_jobs { return $_[0]->{_API_enable_batch_jobs}; } # Compile time generation of setters for the properties documented above UNITCHECK { @@ -610,6 +638,8 @@ UNITCHECK { _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_API_enable_add_api_user', '_API_enable_add_api_user', \&untaint_bool ); + _create_setter( '_set_API_enable_batch_jobs', '_API_enable_batch_jobs', \&untaint_bool ); } =head2 new_DB diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 6f7b6541f..c660c75cc 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -14,6 +14,7 @@ use Zonemaster::Engine::Net::IP; our @EXPORT_OK = qw( untaint_abs_path + untaint_bool untaint_engine_type untaint_ip_address untaint_ipv4_address @@ -35,6 +36,7 @@ our %EXPORT_TAGS = ( untaint => [ qw( untaint_abs_path + untaint_bool untaint_engine_type untaint_ip_address untaint_ipv4_address @@ -96,6 +98,9 @@ Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; +# Boolean 0 or 1 +Readonly my $BOOL_RE => qr/^(0|1)$/; + sub joi { return JSON::Validator::Joi->new; } @@ -317,6 +322,11 @@ sub untaint_profile_name { return _untaint_pat( $value, $PROFILE_NAME_RE ); } +sub untaint_bool { + my ( $value ) = @_; + return _untaint_pat( $value, $BOOL_RE ); +} + sub _untaint_pat { my ( $value, @patterns ) = @_; @@ -342,4 +352,6 @@ sub _untaint_pred { } } + + 1; diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 9aba9d6c9..4f74a7c2d 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -125,24 +125,28 @@ my $router = router { handler => $handler, action => "get_test_history" }; +}; -############ BATCH MODE #################### - - connect "add_api_user" => { +if ($config->API_enable_add_api_user) { + $log->info('Enabling add_api_user method'); + $router->connect("add_api_user", { handler => $handler, action => "add_api_user" - }; + }); +} - connect "add_batch_job" => { +if ($config->API_enable_batch_jobs) { + $log->info('Enabling add_batch_job and get_batch_job_result methods'); + $router->connect("add_batch_job", { handler => $handler, action => "add_batch_job" - }; + }); - connect "get_batch_job_result" => { + $router->connect("get_batch_job_result", { handler => $handler, action => "get_batch_job_result" - }; -}; + }); +} my $dispatch = JSON::RPC::Dispatch->new( router => $router, diff --git a/share/backend_config.ini b/share/backend_config.ini index daf586ec9..5b4827760 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -31,6 +31,10 @@ database_file = /var/lib/zonemaster/db.sqlite # #maximal_number_of_retries=3 +[API] +enable_add_api_user = 0 +enable_add_batch_job = 0 + [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 2b013aa35c02e517734f419b7a70849fef57ab47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 10 Aug 2021 13:37:09 +0200 Subject: [PATCH 272/424] update default config --- share/backend_config.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index 5b4827760..6d6eee306 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -32,8 +32,10 @@ database_file = /var/lib/zonemaster/db.sqlite #maximal_number_of_retries=3 [API] -enable_add_api_user = 0 -enable_add_batch_job = 0 + +# Uncomment to enable the user and batch API methods. +# enable_add_api_user = 1 +# enable_add_batch_job = 1 [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 58b41732be22a4b90643e5322b8433358a4d4ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 16 Aug 2021 11:14:27 +0200 Subject: [PATCH 273/424] fix default config --- share/backend_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index 6d6eee306..1156b55a5 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -35,7 +35,7 @@ database_file = /var/lib/zonemaster/db.sqlite # Uncomment to enable the user and batch API methods. # enable_add_api_user = 1 -# enable_add_batch_job = 1 +# enable_batch_jobs = 1 [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 800cb1fd9f7476a085f90e3e7535a550d3790679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 18 Aug 2021 11:01:57 +0200 Subject: [PATCH 274/424] allow more values for boolean --- lib/Zonemaster/Backend/Validator.pm | 12 +++++++++--- share/backend_config.ini | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index c660c75cc..1d7ef0046 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -98,8 +98,10 @@ Readonly my $RELAXED_DOMAIN_NAME_RE => qr/^[.]$|^.{2,254}$/; Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; -# Boolean 0 or 1 -Readonly my $BOOL_RE => qr/^(0|1)$/; +# Boolean +Readonly my $BOOL_TRUE_RE => qr/^(true|yes|1)$/i; +Readonly my $BOOL_FALSE_RE => qr/^(false|no|0)$/i; +Readonly my $BOOL_RE => qr/^$BOOL_TRUE_RE|$BOOL_FALSE_RE$/i; sub joi { return JSON::Validator::Joi->new; @@ -324,7 +326,11 @@ sub untaint_profile_name { sub untaint_bool { my ( $value ) = @_; - return _untaint_pat( $value, $BOOL_RE ); + + my $ret; + $ret = 1 if defined _untaint_pat( $value, $BOOL_TRUE_RE ); + $ret = 0 if defined _untaint_pat( $value, $BOOL_FALSE_RE ); + return $ret; } sub _untaint_pat { diff --git a/share/backend_config.ini b/share/backend_config.ini index 1156b55a5..a61056720 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -34,8 +34,8 @@ database_file = /var/lib/zonemaster/db.sqlite [API] # Uncomment to enable the user and batch API methods. -# enable_add_api_user = 1 -# enable_batch_jobs = 1 +# enable_add_api_user = yes +# enable_batch_jobs = yes [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 81b5e7d2e1bb0ca696908a446e7631443b349b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 23 Aug 2021 09:19:41 +0200 Subject: [PATCH 275/424] remove numeric value for boolean --- docs/Configuration.md | 6 ++++-- lib/Zonemaster/Backend/Config.pm | 4 ++-- lib/Zonemaster/Backend/Validator.pm | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index d871abd3a..fd574e16c 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -59,13 +59,15 @@ Available keys: `enable_batch_jobs`, `enable_add_api_user`. Boolean value to enable the batch jobs methods of the API. -Accpected values: 0 or 1, default to 0 (disabled). +Accpected values: `yes` (or `true`) or `no` (or `false`), +default to `no` (disabled). ### enable_add_api_user Boolean value to enable the `add_api_user` method of the API. -Accpected values: 0 or 1, default to 0 (disabled). +Accpected values: `yes` (or `true`) or `no` (or `false`), +default to `no` (disabled). ## DB section diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 4f78a16ce..2c3e59b87 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -145,8 +145,8 @@ sub parse { $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_API_enable_add_api_user( '0' ); - $obj->_set_API_enable_batch_jobs( '0' ); + $obj->_set_API_enable_add_api_user( 'no' ); + $obj->_set_API_enable_batch_jobs( 'no' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 1d7ef0046..7572f0682 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -99,8 +99,8 @@ Readonly my $TEST_ID_RE => qr/^[0-9a-f]{16}$/; Readonly my $USERNAME_RE => qr/^[a-z0-9-.@]{1,50}$/i; # Boolean -Readonly my $BOOL_TRUE_RE => qr/^(true|yes|1)$/i; -Readonly my $BOOL_FALSE_RE => qr/^(false|no|0)$/i; +Readonly my $BOOL_TRUE_RE => qr/^(true|yes)$/i; +Readonly my $BOOL_FALSE_RE => qr/^(false|no)$/i; Readonly my $BOOL_RE => qr/^$BOOL_TRUE_RE|$BOOL_FALSE_RE$/i; sub joi { From 6369e1baa1b7a7382291e26db91c5a6a9ac1e407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 25 Aug 2021 15:58:39 +0200 Subject: [PATCH 276/424] change api section to rpcapi --- docs/Configuration.md | 2 +- lib/Zonemaster/Backend/Config.pm | 30 +++++++++++++-------------- script/zonemaster_backend_rpcapi.psgi | 4 ++-- share/backend_config.ini | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index fd574e16c..b523d102b 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -51,7 +51,7 @@ Repeating a key name in one section is forbidden. Each section in `backend_config.ini` is documented below. -## API section +## RPCAPI section Available keys: `enable_batch_jobs`, `enable_add_api_user`. diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 2c3e59b87..26a7af61a 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -127,7 +127,7 @@ sub parse { # Validate section names { - my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'API'); + my %sections = map { $_ => 1 } ( 'DB', 'MYSQL', 'POSTGRESQL', 'SQLITE', 'LANGUAGE', 'PUBLIC PROFILES', 'PRIVATE PROFILES', 'ZONEMASTER', 'RPCAPI'); for my $section ( $ini->Sections ) { if ( !exists $sections{$section} ) { die "config: unrecognized section: $section\n"; @@ -145,8 +145,8 @@ sub parse { $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_API_enable_add_api_user( 'no' ); - $obj->_set_API_enable_batch_jobs( 'no' ); + $obj->_set_RPCAPI_enable_add_api_user( 'no' ); + $obj->_set_RPCAPI_enable_batch_jobs( 'no' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); @@ -277,11 +277,11 @@ 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->( 'API', 'enable_add_api_user' ) ) ) { - $obj->_set_API_enable_add_api_user( $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->( 'API', 'enable_batch_jobs' ) ) ) { - $obj->_set_API_enable_batch_jobs( $value ); + if ( defined( my $value = $get_and_clear->( 'RPCAPI', 'enable_batch_jobs' ) ) ) { + $obj->_set_RPCAPI_enable_batch_jobs( $value ); } if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { @@ -575,19 +575,19 @@ Returns a number. =cut -=head2 API_enable_add_api_user +=head2 RPCAPI_enable_add_api_user Get the value of -L. +L. Return 0 or 1 =cut -=head2 API_enable_batch_jobs +=head2 RPCAPI_enable_batch_jobs Get the value of -L. +L. Return 0 or 1 @@ -615,8 +615,8 @@ sub ZONEMASTER_lock_on_queue { return $_[0]->{_ZONEMA 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 API_enable_add_api_user { return $_[0]->{_API_enable_add_api_user}; } -sub API_enable_batch_jobs { return $_[0]->{_API_enable_batch_jobs}; } +sub RPCAPI_enable_add_api_user { return $_[0]->{_RPCAPI_enable_add_api_user}; } +sub RPCAPI_enable_batch_jobs { return $_[0]->{_RPCAPI_enable_batch_jobs}; } # Compile time generation of setters for the properties documented above UNITCHECK { @@ -638,8 +638,8 @@ UNITCHECK { _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_API_enable_add_api_user', '_API_enable_add_api_user', \&untaint_bool ); - _create_setter( '_set_API_enable_batch_jobs', '_API_enable_batch_jobs', \&untaint_bool ); + _create_setter( '_set_RPCAPI_enable_add_api_user', '_RPCAPI_enable_add_api_user', \&untaint_bool ); + _create_setter( '_set_RPCAPI_enable_batch_jobs', '_RPCAPI_enable_batch_jobs', \&untaint_bool ); } =head2 new_DB diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 4f74a7c2d..3d01a6aa9 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -127,7 +127,7 @@ my $router = router { }; }; -if ($config->API_enable_add_api_user) { +if ($config->RPCAPI_enable_add_api_user) { $log->info('Enabling add_api_user method'); $router->connect("add_api_user", { handler => $handler, @@ -135,7 +135,7 @@ if ($config->API_enable_add_api_user) { }); } -if ($config->API_enable_batch_jobs) { +if ($config->RPCAPI_enable_batch_jobs) { $log->info('Enabling add_batch_job and get_batch_job_result methods'); $router->connect("add_batch_job", { handler => $handler, diff --git a/share/backend_config.ini b/share/backend_config.ini index a61056720..0083e4f57 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -31,9 +31,9 @@ database_file = /var/lib/zonemaster/db.sqlite # #maximal_number_of_retries=3 -[API] +[RPCAPI] -# Uncomment to enable the user and batch API methods. +# Uncomment to enable the user and batch RPCAPI methods. # enable_add_api_user = yes # enable_batch_jobs = yes From 8a70475872b7d62017dd75c2ed7c34e5ff8359c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 8 Sep 2021 15:38:41 +0200 Subject: [PATCH 277/424] update default configuration --- share/backend_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index 0083e4f57..6450e1fc7 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -33,7 +33,7 @@ database_file = /var/lib/zonemaster/db.sqlite [RPCAPI] -# Uncomment to enable the user and batch RPCAPI methods. +# Uncomment to enable API method "add_api_user" # enable_add_api_user = yes # enable_batch_jobs = yes From 0f48b878bc036dd066f68f5ecc7bb980c26743ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 9 Sep 2021 12:02:31 +0200 Subject: [PATCH 278/424] update default config --- share/backend_config.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index 6450e1fc7..a0f07bf59 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -34,8 +34,9 @@ database_file = /var/lib/zonemaster/db.sqlite [RPCAPI] # Uncomment to enable API method "add_api_user" -# enable_add_api_user = yes -# enable_batch_jobs = yes +#enable_add_api_user = yes +# Uncomment to enable the API methods "add_batch_job" and "get_batch_job_result" +#enable_batch_jobs = yes [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 1e5bb8149a62f85549dc946430f492738343faa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 9 Sep 2021 13:31:25 +0200 Subject: [PATCH 279/424] rename setting --- docs/Configuration.md | 6 +++--- lib/Zonemaster/Backend/Config.pm | 16 ++++++++-------- script/zonemaster_backend_rpcapi.psgi | 14 +++++++------- share/backend_config.ini | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index b523d102b..fd3c21742 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -53,11 +53,11 @@ Each section in `backend_config.ini` is documented below. ## RPCAPI section -Available keys: `enable_batch_jobs`, `enable_add_api_user`. +Available keys: `enable_add_batch_job`, `enable_add_api_user`. -### enable_batch_jobs +### enable_add_batch_job -Boolean value to enable the batch jobs methods of the API. +Boolean value to enable the `enable_add_batch_job` methods of the API. Accpected values: `yes` (or `true`) or `no` (or `false`), default to `no` (disabled). diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 26a7af61a..870332103 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -146,7 +146,7 @@ sub parse { $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_batch_jobs( 'no' ); + $obj->_set_RPCAPI_enable_add_batch_job( 'yes' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); @@ -280,8 +280,8 @@ sub parse { 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_batch_jobs' ) ) ) { - $obj->_set_RPCAPI_enable_batch_jobs( $value ); + if ( defined( my $value = $get_and_clear->( 'RPCAPI', 'enable_add_batch_job' ) ) ) { + $obj->_set_RPCAPI_enable_add_batch_job( $value ); } if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { @@ -584,10 +584,10 @@ Return 0 or 1 =cut -=head2 RPCAPI_enable_batch_jobs +=head2 RPCAPI_enable_add_batch_job Get the value of -L. +L. Return 0 or 1 @@ -615,8 +615,8 @@ sub ZONEMASTER_lock_on_queue { return $_[0]->{_ZONEMA 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 RPCAPI_enable_add_api_user { return $_[0]->{_RPCAPI_enable_add_api_user}; } -sub RPCAPI_enable_batch_jobs { return $_[0]->{_RPCAPI_enable_batch_jobs}; } +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 { @@ -639,7 +639,7 @@ UNITCHECK { _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_RPCAPI_enable_add_api_user', '_RPCAPI_enable_add_api_user', \&untaint_bool ); - _create_setter( '_set_RPCAPI_enable_batch_jobs', '_RPCAPI_enable_batch_jobs', \&untaint_bool ); + _create_setter( '_set_RPCAPI_enable_add_batch_job', '_RPCAPI_enable_add_batch_job', \&untaint_bool ); } =head2 new_DB diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 3d01a6aa9..7a6dbd91e 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -125,6 +125,11 @@ my $router = router { handler => $handler, action => "get_test_history" }; + + connect "get_batch_job_result" => { + handler => $handler, + action => "get_batch_job_result" + }; }; if ($config->RPCAPI_enable_add_api_user) { @@ -135,17 +140,12 @@ if ($config->RPCAPI_enable_add_api_user) { }); } -if ($config->RPCAPI_enable_batch_jobs) { - $log->info('Enabling add_batch_job and get_batch_job_result methods'); +if ($config->RPCAPI_enable_add_batch_job) { + $log->info('Enabling add_batch_job method'); $router->connect("add_batch_job", { handler => $handler, action => "add_batch_job" }); - - $router->connect("get_batch_job_result", { - handler => $handler, - action => "get_batch_job_result" - }); } my $dispatch = JSON::RPC::Dispatch->new( diff --git a/share/backend_config.ini b/share/backend_config.ini index a0f07bf59..598dc206d 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -35,8 +35,8 @@ database_file = /var/lib/zonemaster/db.sqlite # Uncomment to enable API method "add_api_user" #enable_add_api_user = yes -# Uncomment to enable the API methods "add_batch_job" and "get_batch_job_result" -#enable_batch_jobs = yes +# Uncomment to enable API method "add_batch_job" +#enable_add_batch_job = yes [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 35b5053bce319ef5bd3809a6524d91c0379d0517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 13 Sep 2021 16:40:18 +0200 Subject: [PATCH 280/424] fix typo --- docs/Configuration.md | 4 ++-- lib/Zonemaster/Backend/Config.pm | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index fd3c21742..3d9512e2c 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -59,14 +59,14 @@ Available keys: `enable_add_batch_job`, `enable_add_api_user`. Boolean value to enable the `enable_add_batch_job` methods of the API. -Accpected values: `yes` (or `true`) or `no` (or `false`), +Accepted values: `yes` (or `true`) or `no` (or `false`), default to `no` (disabled). ### enable_add_api_user Boolean value to enable the `add_api_user` method of the API. -Accpected values: `yes` (or `true`) or `no` (or `false`), +Accepted values: `yes` (or `true`) or `no` (or `false`), default to `no` (disabled). diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 870332103..f0e0c52c0 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -146,7 +146,7 @@ sub parse { $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->_set_RPCAPI_enable_add_batch_job( 'no' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); From c1addefe77c68e99b6ec01ea8809324474881b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 14 Sep 2021 14:20:27 +0200 Subject: [PATCH 281/424] add dependency in makefile.pl --- Makefile.PL | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.PL b/Makefile.PL index 287e35110..f737f5f3f 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -33,6 +33,7 @@ requires 'Try::Tiny' => 0.12, 'Zonemaster::Engine' => 4.002, 'Zonemaster::LDNS' => 2.002, + 'Plack::Middleware::ReverseProxy' => 0, ; test_requires 'DBD::SQLite'; From 7b2d06e796921722410cf39e795af4414c5a6be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 23 Sep 2021 09:59:08 +0200 Subject: [PATCH 282/424] remove new deps instruction --- docs/upgrade_db_zonemaster_backend_ver_7.0.0.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md index fd4b9d592..2acf456f6 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md @@ -8,6 +8,7 @@ Run cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl ``` +<<<<<<< HEAD ### MySQL (or MariaDB) @@ -26,3 +27,5 @@ Run cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') perl patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl ``` +======= +>>>>>>> 10ff7f7 (remove new deps instruction) From 138d31e21bfaa069bde489541103ead6302981f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 23 Sep 2021 13:52:22 +0200 Subject: [PATCH 283/424] update according to comments --- docs/Configuration.md | 2 +- docs/upgrade_db_zonemaster_backend_ver_7.0.0.md | 3 --- lib/Zonemaster/Backend/Config.pm | 2 +- share/backend_config.ini | 4 ++-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 3d9512e2c..82994685f 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -67,7 +67,7 @@ default to `no` (disabled). Boolean value to enable the `add_api_user` method of the API. Accepted values: `yes` (or `true`) or `no` (or `false`), -default to `no` (disabled). +default to `yes` (enabled). ## DB section diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md index 2acf456f6..fd4b9d592 100644 --- a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md @@ -8,7 +8,6 @@ Run cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl ``` -<<<<<<< HEAD ### MySQL (or MariaDB) @@ -27,5 +26,3 @@ Run cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') perl patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl ``` -======= ->>>>>>> 10ff7f7 (remove new deps instruction) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index f0e0c52c0..870332103 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -146,7 +146,7 @@ sub parse { $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( 'no' ); + $obj->_set_RPCAPI_enable_add_batch_job( 'yes' ); $obj->_add_LANGUAGE_locale( 'en_US' ); $obj->_add_public_profile( 'default', undef ); diff --git a/share/backend_config.ini b/share/backend_config.ini index 598dc206d..66dbe8444 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -35,8 +35,8 @@ database_file = /var/lib/zonemaster/db.sqlite # Uncomment to enable API method "add_api_user" #enable_add_api_user = yes -# Uncomment to enable API method "add_batch_job" -#enable_add_batch_job = yes +# Uncomment to disable API method "add_batch_job" +#enable_add_batch_job = no [LANGUAGE] locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE From 1111edae5407e1a49790f20369189980a789f070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 23 Sep 2021 14:24:55 +0200 Subject: [PATCH 284/424] fix doc --- docs/Configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 82994685f..3f980c0e1 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -60,14 +60,14 @@ Available keys: `enable_add_batch_job`, `enable_add_api_user`. Boolean value to enable the `enable_add_batch_job` methods of the API. Accepted values: `yes` (or `true`) or `no` (or `false`), -default to `no` (disabled). +default to `yes` (enabled). ### enable_add_api_user Boolean value to enable the `add_api_user` method of the API. Accepted values: `yes` (or `true`) or `no` (or `false`), -default to `yes` (enabled). +default to `no` (disabled). ## DB section From 150fa36d048676bbcbf1226f228dee1c16e34ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 8 Sep 2021 14:54:00 +0200 Subject: [PATCH 285/424] fix shared db handler --- script/zonemaster_backend_testagent | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index eab4507a7..53893d409 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -132,6 +132,8 @@ 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 ) { $self->pm->reap_finished_children(); # Reaps terminated child processes From f64952dfc545c8fc7d92e7449c185c2f2be01473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 23 Sep 2021 14:56:43 +0200 Subject: [PATCH 286/424] make sqlite db class behave the same way as others --- lib/Zonemaster/Backend/DB.pm | 40 +++++++++++++++++++++++++ lib/Zonemaster/Backend/DB/MySQL.pm | 39 ------------------------ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 40 ------------------------- lib/Zonemaster/Backend/DB/SQLite.pm | 10 +++---- 4 files changed, 44 insertions(+), 85 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 3398394d1..c5fb962d3 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -31,6 +31,30 @@ requires qw( 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. @@ -50,6 +74,22 @@ sub get_db_class { 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 ) = @_; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 9027c70a5..b778813ea 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -14,30 +14,6 @@ use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -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, -); - =head1 CLASS METHODS =head2 from_config @@ -79,21 +55,6 @@ sub from_config { ); } -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 create_db { my ( $self ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index d14bb57a8..2452179b7 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -14,30 +14,6 @@ use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -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, -); - =head1 CLASS METHODS =head2 from_config @@ -75,22 +51,6 @@ sub from_config { ); } -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 create_db { my ( $self ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 1462ab097..3e2b1b2d2 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -15,11 +15,6 @@ use Zonemaster::Backend::Errors; with 'Zonemaster::Backend::DB'; -has 'dbh' => ( - is => 'rw', - isa => 'DBI::db', -); - =head1 CLASS METHODS =head2 from_config @@ -41,7 +36,10 @@ sub from_config { return $class->new( { - dbh => $dbh, + data_source_name => $data_source_name, + user => '', + password => '', + dbhandle => $dbh, } ); } From 8be93578f3fc2bbef102b958abcbbcaa6cbd6738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Fri, 24 Sep 2021 17:48:36 +0200 Subject: [PATCH 287/424] fix double disconnect --- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 3e2b1b2d2..f78edac6c 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -46,7 +46,7 @@ sub from_config { sub DEMOLISH { my ( $self ) = @_; - $self->dbh->disconnect() if $self->dbh; + $self->dbh->disconnect() if defined $self->dbhandle && $self->dbhandle->ping; } sub create_db { From f2f98d13f19fb22fb7054c916ebc4aa9f13716a3 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Sat, 25 Sep 2021 14:21:46 +0200 Subject: [PATCH 288/424] Adds missing entries in table of contents --- docs/Configuration.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Configuration.md b/docs/Configuration.md index 3f980c0e1..6f61ad99d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -3,6 +3,9 @@ ## 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) From 97cfa2574e021da3692c61f4d4a7fe14420e6436 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Sat, 25 Sep 2021 15:41:08 +0200 Subject: [PATCH 289/424] Updates document * Updates link text for IPv6 address format. * Updates specification "add_api_user" method and "add_batch_job" method, respectively. * Adds reference to "enable_add_api_user" and "enable_add_batch_job" configuration keys. * Adds examples of error messages for both methods. * Adds new links in link section. * Sorts links in link section. --- docs/API.md | 126 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 22 deletions(-) diff --git a/docs/API.md b/docs/API.md index b02aa9031..da69b678a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -201,7 +201,7 @@ Basic data type: string This parameter is a string that is either - a valid IPv4 address in [dot-decimal notation] ; - - a valid IPv6 address in [recommend text format for IPv6 addresses]. + - a valid IPv6 address in [recommended text format][RFC 5952] for IPv6 addresses. ### Language tag @@ -1100,13 +1100,13 @@ An object with the following properties: ## 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 an API +user and key must be created by this method. -Add a new *user* +This method is not avaialable if [`RPCAPI.enable_add_api_user`] is disabled +(disabled by default). This method is not available unless the connection to +RPCAPI is over localhost. -This method requires the *administrative* *privilege level*. Example request: ```json @@ -1145,6 +1145,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. > @@ -1152,29 +1153,82 @@ 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: +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 + "result": 0, + "id": 1, + "jsonrpc": "2.0" } ``` +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 avaialable 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. +API user can be create with the [`add_api_user`](#API-method-add_api_user) +method. An *api user* can only have one un-finished *batch* at a time. *Tests* enqueud using this method are assigned a *priority* of 5. @@ -1238,11 +1292,36 @@ A *batch id*. #### `"error"` -* You can't create a new batch job. - A *batch* with unfinished *tests* already exists for this *api user*. +* You cannot create a new batch job if a *batch* with unfinished *tests* already + exists for this *api user*. * 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 wrong user name 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` @@ -1367,21 +1446,24 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [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 +[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 -[Validation error data]: #validation-error-data -[Dot-decimal notation]: https://en.wikipedia.org/wiki/Dot-decimal_notation -[Recommend text format for IPv6 addresses]: https://datatracker.ietf.org/doc/html/rfc5952 -[JSON Pointer]: https://datatracker.ietf.org/doc/html/rfc6901 [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 -[Severity Level Definitions]: https://github.com/zonemaster/zonemaster/blob/master/docs/specifications/tests/SeverityLevelDefinitions.md + From 76e232e6fe7a61b36a5101c37c485f3ba2958714 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 27 Sep 2021 13:19:19 +0200 Subject: [PATCH 290/424] Adds another error message for "add_batch_job" --- docs/API.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/API.md b/docs/API.md index da69b678a..ee85055e9 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1297,6 +1297,23 @@ A *batch id*. * 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 current user: +```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 +} + +``` + Trying to add a batch when wrong user name or API key is used: ```json { From 3e008056c5acc9393bc92778ca21565cc351a00e Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 27 Sep 2021 16:58:50 +0200 Subject: [PATCH 291/424] Editorial updates and makes doc consistent on "username" and "api key" --- docs/API.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/API.md b/docs/API.md index ee85055e9..d30d2921e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -113,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 @@ -1100,12 +1102,12 @@ An object with the following properties: ## API method: `add_api_user` -In order to use the [`add_batch_job`](#API-method-add_batch_job) method an API -user and key must be created by this method. +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. -This method is not avaialable if [`RPCAPI.enable_add_api_user`] is disabled +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. +RPCAPI is over localhost (*administrative* method). Example request: @@ -1135,8 +1137,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"` @@ -1224,11 +1227,12 @@ Trying to add a user when the method is disabled: 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. -This method is not avaialable if [`RPCAPI.enable_add_batch_job`] is disabled +This method is not available if [`RPCAPI.enable_add_batch_job`] is disabled (enabled by default). -API user can be create with the [`add_api_user`](#API-method-add_api_user) -method. An *api user* can only have one un-finished *batch* at a time. +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. @@ -1266,7 +1270,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: `{}`) @@ -1293,11 +1297,12 @@ A *batch id*. #### `"error"` * You cannot create a new batch job if a *batch* with unfinished *tests* already - exists for this *api user*. + 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 current user: +Trying to add a batch when a batch is still running for the *username* in the +request: ```json { "jsonrpc": "2.0", @@ -1314,7 +1319,7 @@ Trying to add a batch when a batch is still running for the current user: ``` -Trying to add a batch when wrong user name or API key is used: +Trying to add a batch when wrong *username* or *api key* is used: ```json { "error": { From 16638b2c3ec79b18023bc655fa07c28ba5101146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 28 Sep 2021 13:57:44 +0200 Subject: [PATCH 292/424] add tests for validate_params --- lib/Zonemaster/Backend/RPCAPI.pm | 4 +- t/parameters_validation.t | 197 +++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 t/parameters_validation.t diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index a6033debe..0bf355f93 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -31,7 +31,7 @@ use Zonemaster::Backend::Translator; use Zonemaster::Backend::Validator; my $zm_validator = Zonemaster::Backend::Validator->new; -my %json_schemas; +our %json_schemas; our %extra_validators; my $recursor = Zonemaster::Engine::Recursor->new; @@ -700,8 +700,6 @@ sub validate_params { # Customize error message from json validation foreach my $err ( @json_validation_error ) { - print Data::Dumper::Dumper($err); - my $message = $err->message; my @details = @{$err->details}; diff --git a/t/parameters_validation.t b/t/parameters_validation.t new file mode 100644 index 000000000..b9deaab54 --- /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, $test_cases ) = @_; + + subtest "Method $method" => sub { + for my $test_case (@$test_cases) { + subtest 'Test case: ' . $test_case->{name} => sub { + my @res = $rpcapi->validate_params( $method, $test_case->{input}); + is_deeply(\@res, $test_case->{output}, 'Matched validation output' ) or diag( encode_json \@res); + }; + } + }; +} + +subtest 'Test JSON schema' => sub { + local $Zonemaster::Backend::RPCAPI::json_schemas{test_joi} = joi->new->object->strict->props( + hostname => joi->new->string->max(10)->required + ); + + local $Zonemaster::Backend::RPCAPI::json_schemas{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_cases; + test_validation 'test_raw_schema', $test_cases; +}; + +subtest 'Test custom error message' => sub { + local $Zonemaster::Backend::RPCAPI::json_schemas{test_custom_error} = { + 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_cases; +}; + +subtest 'Test extra validators' => sub { + local $Zonemaster::Backend::RPCAPI::extra_validators{test_extra_validator} = sub { + my ($self, $input) = @_; + my @errors; + if ( $input->{answer} != 42 ) { + push @errors, { path => '/answer', message => 'Not the expected answer' }; + } + + return @errors; + }; + + local $Zonemaster::Backend::RPCAPI::json_schemas{test_extra_validator} = { + 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_cases; +}; From 088f5498a14c0cf711679bb7d70b3500845ac464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Tue, 28 Sep 2021 14:11:38 +0200 Subject: [PATCH 293/424] update manifest --- MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST b/MANIFEST index 28b38eb1a..f688cd96d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -69,6 +69,7 @@ share/zm_rpcapi-bsd share/zm_testagent-bsd t/config.t t/db.t +t/parameters_validation.t t/test01.data t/test01.t t/test_validate_syntax.t From 1093c435f051b419f09a09a67006e2d157cbf1f6 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 9 Aug 2021 18:07:41 +0200 Subject: [PATCH 294/424] Create tables if not exist (SQLite) --- lib/Zonemaster/Backend/DB/SQLite.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 23d87bb63..36a14d0c5 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -56,7 +56,7 @@ sub create_db { # TEST RESULTS #################################################################### $self->dbh->do( - 'CREATE TABLE test_results ( + 'CREATE TABLE IF NOT EXISTS test_results ( id integer PRIMARY KEY AUTOINCREMENT, hash_id VARCHAR(16) DEFAULT NULL, domain VARCHAR(255) NOT NULL, @@ -97,7 +97,7 @@ sub create_db { # BATCH JOBS #################################################################### $self->dbh->do( - 'CREATE TABLE batch_jobs ( + '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 @@ -110,7 +110,7 @@ sub create_db { # USERS #################################################################### $self->dbh->do( - 'CREATE TABLE users ( + 'CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username varchar(128), api_key varchar(512), From 9244e3e64c9e757453498fce515637fc0dd183d2 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 15:41:59 +0200 Subject: [PATCH 295/424] Create indexes if not exist (SQLite) --- lib/Zonemaster/Backend/DB/SQLite.pm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 36a14d0c5..0f0f60ae4 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -77,19 +77,19 @@ sub create_db { ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; $self->dbh->do( - 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' + 'CREATE INDEX IF NOT EXISTS test_results__hash_id ON test_results (hash_id)' ); $self->dbh->do( - 'CREATE INDEX test_results__fingerprint ON test_results (params_deterministic_hash)' + 'CREATE INDEX IF NOT EXISTS test_results__fingerprint ON test_results (params_deterministic_hash)' ); $self->dbh->do( - 'CREATE INDEX test_results__batch_id_progress ON test_results (batch_id, progress)' + 'CREATE INDEX IF NOT EXISTS test_results__batch_id_progress ON test_results (batch_id, progress)' ); $self->dbh->do( - 'CREATE INDEX test_results__progress ON test_results (progress)' + 'CREATE INDEX IF NOT EXISTS test_results__progress ON test_results (progress)' ); $self->dbh->do( - 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' + 'CREATE INDEX IF NOT EXISTS test_results__domain_undelegated ON test_results (domain, undelegated)' ); From aceac88baf9ecd7b5303fb420811a3d6ed6c4d55 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 14:11:08 +0200 Subject: [PATCH 296/424] Create tables if not exist (MySQL, PostgreSQL) --- lib/Zonemaster/Backend/DB/MySQL.pm | 6 +++--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 690ad00b7..7ee3190b5 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -65,7 +65,7 @@ sub create_db { # TEST RESULTS #################################################################### $dbh->do( - 'CREATE TABLE test_results ( + 'CREATE TABLE IF NOT EXISTS test_results ( id integer AUTO_INCREMENT PRIMARY KEY, hash_id VARCHAR(16) DEFAULT NULL, domain varchar(255) NOT NULL, @@ -119,7 +119,7 @@ sub create_db { # BATCH JOBS #################################################################### $dbh->do( - 'CREATE TABLE batch_jobs ( + '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 @@ -132,7 +132,7 @@ sub create_db { # USERS #################################################################### $dbh->do( - 'CREATE TABLE users ( + 'CREATE TABLE IF NOT EXISTS users ( id integer AUTO_INCREMENT primary key, username varchar(128), api_key varchar(512), diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 0dfd16451..e36745c1c 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -60,7 +60,7 @@ sub create_db { # TEST RESULTS #################################################################### $dbh->do( - 'CREATE TABLE test_results ( + 'CREATE TABLE IF NOT EXISTS 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, @@ -100,7 +100,7 @@ sub create_db { # BATCH JOBS #################################################################### $dbh->do( - 'CREATE TABLE batch_jobs ( + '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 @@ -113,7 +113,7 @@ sub create_db { # USERS #################################################################### $dbh->do( - 'CREATE TABLE users ( + 'CREATE TABLE IF NOT EXISTS users ( id serial PRIMARY KEY, user_info json ) From e12d9ddc7c92aaf217b2e8116ee37482d501dc70 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 14:12:04 +0200 Subject: [PATCH 297/424] Manually create indexes if not exist (MySQL) The clause IF NOT EXISTS is not available for CREATE INDEX in MySQL (which is the engine used with FreeBSD). --- lib/Zonemaster/Backend/DB/MySQL.pm | 46 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 7ee3190b5..d2f80fac0 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -98,21 +98,37 @@ sub create_db { ' ); - $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__progress ON test_results (progress)' - ); - $dbh->do( - 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' - ); + # 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__params_deterministic_hash}) ) { + $dbh->do( + 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' + ); + } + 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)' + ); + } #################################################################### From 09670de12d2f69d88d50d557c3e2637575aa5dd8 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 15:18:51 +0200 Subject: [PATCH 298/424] Manually create the trigger if not exists (MySQL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clause IF NOT EXISTS is not available for CREATE TRIGGER in MariaDB < 10.1.4 and MySQL (which is the engine used with FreeBSD). --- lib/Zonemaster/Backend/DB/MySQL.pm | 32 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index d2f80fac0..29f43aad2 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -85,18 +85,26 @@ sub create_db { ' ); - $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; - ' - ); + + # Manually create the trigger if it does not exist + # the clause IF NOT EXISTS is not available for MariaDB < 10.1.4 and MySQL + + # retrieve all triggers by name + my $triggers = $dbh->selectall_hashref( 'SHOW TRIGGERS', 'Trigger' ); + if ( not exists($triggers->{before_insert_test_results}) ) { + $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; + ' + ); + } # Manually create the index if it does not exist # the clause IF NOT EXISTS is not available for MySQL (used with FreeBSD) From e0ccd60bf22618df5fc2cc0ba9db729c0352ed82 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 15:34:11 +0200 Subject: [PATCH 299/424] Manually create indexes if not exist (PostgreSQL) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clause IF NOT EXISTS is not available for CREATE INDEX in PostgreSQL < 9.5 --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 45 ++++++++++++++++--------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e36745c1c..83e0192a7 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -79,21 +79,36 @@ sub create_db { ' ); - $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__progress ON test_results (progress)' - ); - $dbh->do( - "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated'))" - ); + # Manually create the index if it does not exist + # the clause IF NOT EXISTS is not available for PostgreSQL < 9.5 + + # retrieve all indexes by key name + my $indexes = $dbh->selectall_hashref( "SELECT indexname FROM pg_indexes WHERE tablename = 'test_results'", 'indexname' ); + 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__params_deterministic_hash}) ) { + $dbh->do( + 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' + ); + } + 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 ((params->>'domain'), (params->>'undelegated'))" + ); + } #################################################################### From f1d417ca5ab35522ef207ca6a89df178e1001669 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 21 Sep 2021 11:11:21 +0200 Subject: [PATCH 300/424] Refactoring --- lib/Zonemaster/Backend/DB/SQLite.pm | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 0f0f60ae4..031c55d8b 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -52,10 +52,12 @@ sub DEMOLISH { sub create_db { my ( $self ) = @_; + my $dbh = $self->dbh; + #################################################################### # TEST RESULTS #################################################################### - $self->dbh->do( + $dbh->do( 'CREATE TABLE IF NOT EXISTS test_results ( id integer PRIMARY KEY AUTOINCREMENT, hash_id VARCHAR(16) DEFAULT NULL, @@ -74,21 +76,21 @@ sub create_db { nb_retries integer NOT NULL DEFAULT 0 ) ' - ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; - $self->dbh->do( + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__hash_id ON test_results (hash_id)' ); - $self->dbh->do( + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__fingerprint ON test_results (params_deterministic_hash)' ); - $self->dbh->do( + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__batch_id_progress ON test_results (batch_id, progress)' ); - $self->dbh->do( + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__progress ON test_results (progress)' ); - $self->dbh->do( + $dbh->do( 'CREATE INDEX IF NOT EXISTS test_results__domain_undelegated ON test_results (domain, undelegated)' ); @@ -96,20 +98,20 @@ sub create_db { #################################################################### # BATCH JOBS #################################################################### - $self->dbh->do( + $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 "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; #################################################################### # USERS #################################################################### - $self->dbh->do( + $dbh->do( 'CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username varchar(128), @@ -117,7 +119,7 @@ sub create_db { user_info json DEFAULT NULL ) ' - ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n"; + ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; return 1; } From b0f24f61644031b5b12e007c4e3fd88ce44f473f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 21 Sep 2021 11:12:06 +0200 Subject: [PATCH 301/424] Die on table creation, use internal error handling --- lib/Zonemaster/Backend/DB/MySQL.pm | 6 +++--- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 +++--- lib/Zonemaster/Backend/DB/SQLite.pm | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 29f43aad2..5347af191 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -83,7 +83,7 @@ sub create_db { nb_retries 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 trigger if it does not exist @@ -149,7 +149,7 @@ sub create_db { 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() ); #################################################################### @@ -163,7 +163,7 @@ sub create_db { user_info blob DEFAULT NULL ) ENGINE=InnoDB; ' - ); + ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); } sub user_exists_in_db { diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 83e0192a7..2990e634e 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -77,7 +77,7 @@ sub create_db { nb_retries integer NOT NULL DEFAULT 0 ) ' - ); + ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL 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 PostgreSQL < 9.5 @@ -121,7 +121,7 @@ sub create_db { 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() ); #################################################################### @@ -133,7 +133,7 @@ sub create_db { user_info json ) ' - ); + ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL error, could not create 'users' table", data => $dbh->errstr() ); } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 031c55d8b..fe010b86d 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -76,7 +76,7 @@ sub create_db { nb_retries integer NOT NULL DEFAULT 0 ) ' - ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; + ) 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)' @@ -105,7 +105,7 @@ sub create_db { creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ) ' - ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; + ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'batch_jobs' table", data => $dbh->errstr() ); #################################################################### @@ -119,7 +119,7 @@ sub create_db { user_info json DEFAULT NULL ) ' - ) or die "SQLite Fatal error: " . $dbh->errstr() . "\n"; + ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'users' table", data => $dbh->errstr() ); return 1; } From 576460642193ddb902c86bb3ced83f71c7a70af2 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 5 Aug 2021 15:27:21 +0200 Subject: [PATCH 302/424] Rename column in "test_results" table From "params_deterministic_hash" to "fingerprint" --- lib/Zonemaster/Backend/DB/MySQL.pm | 18 +++++++++--------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 18 +++++++++--------- lib/Zonemaster/Backend/DB/SQLite.pm | 16 ++++++++-------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 5347af191..bfdf5df89 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -76,7 +76,7 @@ sub create_db { priority integer DEFAULT 10, queue integer DEFAULT 0, progress integer DEFAULT 0, - params_deterministic_hash character varying(32), + fingerprint character varying(32), params blob NOT NULL, results mediumblob DEFAULT NULL, undelegated integer NOT NULL DEFAULT 0, @@ -117,9 +117,9 @@ sub create_db { 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' ); } - if ( not exists($indexes->{test_results__params_deterministic_hash}) ) { + if ( not exists($indexes->{test_results__fingerprint}) ) { $dbh->do( - 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' + 'CREATE INDEX test_results__fingerprint ON test_results (fingerprint)' ); } if ( not exists($indexes->{test_results__batch_id_progress}) ) { @@ -243,7 +243,7 @@ sub create_new_test { $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)) < ? + SELECT hash_id FROM test_results WHERE fingerprint = ? AND (TO_SECONDS(NOW()) - TO_SECONDS(creation_time)) < ? ], undef, $fingerprint, $seconds_between_tests_with_same_params, ); @@ -255,7 +255,7 @@ sub create_new_test { else { $dbh->do( q[ - INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params, domain, test_start_time, undelegated) VALUES (?, ?,?,?,?,?, NOW(),?) + INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, test_start_time, undelegated) VALUES (?, ?,?,?,?,?, NOW(),?) ], undef, $batch_id, @@ -268,7 +268,7 @@ sub create_new_test { ); 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, $fingerprint); + "SELECT id, hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint); $result_id = $hash_id; } @@ -438,12 +438,12 @@ sub add_batch_job { $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, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); + my $sth = $dbh->prepare( 'INSERT INTO test_results (domain, batch_id, priority, queue, fingerprint, params, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?) ' ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -454,7 +454,7 @@ sub add_batch_job { $sth->execute( $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)" ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 2990e634e..df7fa3b0c 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -70,7 +70,7 @@ sub create_db { priority integer DEFAULT 10, queue integer DEFAULT 0, progress integer DEFAULT 0, - params_deterministic_hash varchar(32), + fingerprint varchar(32), params json NOT NULL, undelegated integer NOT NULL DEFAULT 0, results json, @@ -89,9 +89,9 @@ sub create_db { 'CREATE INDEX test_results__hash_id ON test_results (hash_id)' ); } - if ( not exists($indexes->{test_results__params_deterministic_hash}) ) { + if ( not exists($indexes->{test_results__fingerprint}) ) { $dbh->do( - 'CREATE INDEX test_results__params_deterministic_hash ON test_results (params_deterministic_hash)' + 'CREATE INDEX test_results__fingerprint ON test_results (fingerprint)' ); } if ( not exists($indexes->{test_results__batch_id_progress}) ) { @@ -223,11 +223,11 @@ sub create_new_test { my $queue_label = $test_params->{queue}; my $sth = $dbh->prepare( " - INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params, undelegated) + INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, undelegated) SELECT ?, ?, ?, ?, ?, ? WHERE NOT EXISTS ( SELECT * FROM test_results - WHERE params_deterministic_hash = ? + WHERE fingerprint = ? AND creation_time > NOW() - ?::interval )" ); my $nb_inserted = $sth->execute( # @@ -242,7 +242,7 @@ sub create_new_test { ); 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, $fingerprint ); + "SELECT id,hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint ); return $hash_id; } @@ -380,12 +380,12 @@ sub add_batch_job { $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, undelegated) FROM STDIN" ); + $dbh->do( "COPY test_results(batch_id, priority, queue, fingerprint, params, undelegated) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -398,7 +398,7 @@ sub add_batch_job { $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'))" ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index fe010b86d..a0b5d2c74 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -69,7 +69,7 @@ sub create_db { priority integer DEFAULT 10, queue integer DEFAULT 0, progress integer DEFAULT 0, - params_deterministic_hash character varying(32), + fingerprint character varying(32), params text NOT NULL, results text DEFAULT NULL, undelegated boolean NOT NULL DEFAULT false, @@ -81,8 +81,8 @@ sub create_db { $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 (params_deterministic_hash)' + $self->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)' @@ -199,7 +199,7 @@ sub create_new_test { # 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', ?)", + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND test_start_time > DATETIME('now', ?)", undef, $fingerprint, "-$seconds seconds" @@ -216,7 +216,7 @@ sub create_new_test { # 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'; + my $fields = 'hash_id, batch_id, priority, queue, fingerprint, params, domain, test_start_time, undelegated'; $dbh->do( "INSERT INTO test_results ($fields) VALUES (?,?,?,?,?,?,?, datetime('now'),?)", undef, @@ -372,12 +372,12 @@ sub add_batch_job { $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, undelegated) 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; @@ -388,7 +388,7 @@ sub add_batch_job { $sth->execute( substr(md5_hex(time().rand()), 0, 16), $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)" ); From 273ad59d9f161a8579a3b02a7e264f6fd68d4032 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 9 Aug 2021 17:42:52 +0200 Subject: [PATCH 303/424] Set start and end time to NULL by default --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 4 ++-- lib/Zonemaster/Backend/DB/SQLite.pm | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index df7fa3b0c..cb7926e7c 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -65,8 +65,8 @@ sub create_db { 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, + 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, diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index b67490e0c..94c588aa2 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -64,8 +64,8 @@ sub create_db { 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, + test_start_time TIMESTAMP DEFAULT NULL, + test_end_time TIMESTAMP DEFAULT NULL, priority integer DEFAULT 10, queue integer DEFAULT 0, progress integer DEFAULT 0, From 5f1e27b887a1fa3844cabb2df8209c758f1ae0be Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 5 Aug 2021 15:22:20 +0200 Subject: [PATCH 304/424] Update patch script to rename table's column * Rename column "params_deterministic_hash" into "fingerprint" from table "test_results". * Add missing "undelegated" column (PostgreSQL) --- .../patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 11 +++++++++++ ...h_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 14 ++++++++++++++ ...patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index 2f347d67e..da43d63cc 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -16,6 +16,17 @@ sub patch_db { + + # 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 the "undelegated" column my $sth1 = $dbh->prepare('SELECT id, params from test_results', undef); $sth1->execute; while ( my $row = $sth1->fetchrow_hashref ) { diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 147e1394f..21bee6f46 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -17,6 +17,20 @@ sub patch_db { + + # 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 ($@); + + # Add missing "undelegated" column + eval { + $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); + }; + print( "Error while changing DB 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 ) { diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index a8311fc08..5173926b1 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -16,6 +16,15 @@ sub patch_db { + + # Rename column "params_deterministic_hash" into "fingerprint" + # Since SQLite 3.25 (2018-09-15) + eval { + $dbh->do('ALTER TABLE test_results RENAME COLUMN params_deterministic_hash TO fingerprint'); + }; + print( "Error while changing DB 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 ) { From f0e22ecbd2179a33b2a898f2d618879db286cd52 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 9 Aug 2021 17:46:41 +0200 Subject: [PATCH 305/424] Don't set "test_start_time" before running a test Addresses #842 --- lib/Zonemaster/Backend/DB/MySQL.pm | 4 ++-- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 +- lib/Zonemaster/Backend/DB/SQLite.pm | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index bfdf5df89..29b5230a9 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -255,7 +255,7 @@ sub create_new_test { else { $dbh->do( q[ - INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, test_start_time, undelegated) VALUES (?, ?,?,?,?,?, NOW(),?) + INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?) ], undef, $batch_id, @@ -513,7 +513,7 @@ sub process_unfinished_tests_give_up { sub schedule_for_retry { 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); + $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); } sub get_relative_start_time { diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index cb7926e7c..5217dc7a7 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -456,7 +456,7 @@ sub process_unfinished_tests_give_up { sub schedule_for_retry { 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); + $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); } sub get_relative_start_time { diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 94c588aa2..e3fe04570 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -216,9 +216,9 @@ sub create_new_test { # 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, fingerprint, params, domain, test_start_time, undelegated'; + my $fields = 'hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated'; $dbh->do( - "INSERT INTO test_results ($fields) VALUES (?,?,?,?,?,?,?, datetime('now'),?)", + "INSERT INTO test_results ($fields) VALUES (?,?,?,?,?,?,?,?)", undef, $hash_id, $batch_id, @@ -447,7 +447,7 @@ sub process_unfinished_tests_give_up { sub schedule_for_retry { 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); + $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); } sub get_relative_start_time { From 49ec9a08ebbb1a98fb7494dd37fda2295c9e8fde Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 5 Aug 2021 16:02:13 +0200 Subject: [PATCH 306/424] Update index in patch scripts Rename index from "test_results__params_deterministic_hash" into "test_results__fingerprint". --- share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 11 +++++++++++ ...atch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 8 ++++++++ share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 7 +++++++ 3 files changed, 26 insertions(+) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index da43d63cc..bf3ce6c79 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -26,6 +26,17 @@ sub patch_db { }; 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; diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 21bee6f46..126da3916 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -24,6 +24,14 @@ sub patch_db { }; print( "Error while changing DB schema: " . $@ ) if ($@); + # Update index + eval { + # clause IF EXISTS available since PostgreSQL >= 9.2 + $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 ($@); + # Add missing "undelegated" column eval { $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 5173926b1..47d9541b0 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -24,6 +24,13 @@ sub patch_db { }; print( "Error while changing DB schema: " . $@ ) if ($@); + # Update index + eval { + $dbh->do( "DROP INDEX IF EXISTS 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; From 8b3e81f1e38adc4f713bfe3933ff0b30faeb0ba1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 9 Aug 2021 17:56:19 +0200 Subject: [PATCH 307/424] Patch script to update default value (PostgreSQL) --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 126da3916..303a9d1e8 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -32,6 +32,14 @@ sub patch_db { }; 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 "undelegated" column eval { $dbh->do( 'ALTER TABLE test_results ADD COLUMN undelegated integer NOT NULL DEFAULT 0' ); From fc06da95e53321954dd8bf036aa287dd6d84aceb Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 9 Aug 2021 18:36:02 +0200 Subject: [PATCH 308/424] Patch script for SQLite Recreate the "test_results" table --- ..._sqlite_db_zonemaster_backend_ver_7.0.0.pl | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 47d9541b0..8bb737e73 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -17,19 +17,28 @@ sub patch_db { - # Rename column "params_deterministic_hash" into "fingerprint" - # Since SQLite 3.25 (2018-09-15) + # 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 COLUMN params_deterministic_hash TO fingerprint'); - }; - print( "Error while changing DB schema: " . $@ ) if ($@); + $dbh->do('ALTER TABLE test_results RENAME TO test_results_old'); - # Update index - eval { - $dbh->do( "DROP INDEX IF EXISTS test_results__params_deterministic_hash ON test_results" ); - $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); + # create the table + $db->create_db(); + + # populate it + $dbh->do('INSERT INTO test_results SELECT * FROM test_results_old'); + + $dbh->do('DROP TABLE test_results_old'); + + # recreate indexes + $db->create_db(); }; - print( "Error while updating the index: " . $@ ) if ($@); + 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); From a8a4cee3f37cef0a3a62574c1ecd18219a26788b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 5 Aug 2021 17:04:30 +0200 Subject: [PATCH 309/424] Fix computation of test's age (SQLite) Check against "creation_time" to compute the test"s age instead of "test_start_time". --- lib/Zonemaster/Backend/DB/SQLite.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index a0b5d2c74..b67490e0c 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -199,7 +199,7 @@ sub create_new_test { # 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 fingerprint = ? AND test_start_time > DATETIME('now', ?)", + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND creation_time > DATETIME('now', ?)", undef, $fingerprint, "-$seconds seconds" From bfb2243259bf197ce2f3ddb9f1b45636e56f2ce8 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 17:42:39 +0200 Subject: [PATCH 310/424] Manually generate the hash_id before insertion Generate the hash and insert it into the database when creating a new test. Also update the table schema to make the hash_id NOT NULL on all engines (adapt PostgreSQL). --- lib/Zonemaster/Backend/DB/MySQL.pm | 15 ++++++++------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 15 ++++++++------- lib/Zonemaster/Backend/DB/SQLite.pm | 5 +++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 94aedc4c9..4bba14075 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -6,6 +6,7 @@ use Moose; use 5.14.2; use DBI qw(:utils); +use Digest::MD5 qw(md5_hex); use JSON::PP; use Zonemaster::Backend::Validator qw( untaint_ipv6_address ); @@ -66,7 +67,7 @@ sub create_db { $dbh->do( 'CREATE TABLE IF NOT EXISTS test_results ( id integer AUTO_INCREMENT PRIMARY KEY, - hash_id VARCHAR(16) DEFAULT NULL, + hash_id VARCHAR(16) NOT NULL, domain varchar(255) NOT NULL, batch_id integer NULL, creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, @@ -202,9 +203,11 @@ sub create_new_test { $hash_id = $recent_hash_id; } else { + $hash_id = substr(md5_hex(time().rand()), 0, 16); $dbh->do( - "INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?)", + "INSERT INTO test_results (hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?,?)", undef, + $hash_id, $batch_id, $priority, $queue_label, @@ -213,9 +216,6 @@ sub create_new_test { $test_params->{domain}, $undelegated, ); - - ( undef, $hash_id ) = $dbh->selectrow_array( - "SELECT id, hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint); } }; $dbh->do( q[UNLOCK TABLES] ); @@ -369,7 +369,7 @@ sub add_batch_job { 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, fingerprint, params, undelegated) 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; @@ -377,7 +377,8 @@ sub add_batch_job { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - $sth->execute( $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); + 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__fingerprint ON test_results (fingerprint)" ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index b556890c1..2066e12cc 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -6,6 +6,7 @@ use Moose; use 5.14.2; use DBI qw(:utils); +use Digest::MD5 qw(md5_hex); use Encode; use JSON::PP; @@ -62,7 +63,7 @@ sub create_db { $dbh->do( 'CREATE TABLE IF NOT EXISTS test_results ( id serial PRIMARY KEY, - hash_id VARCHAR(16) DEFAULT substring(md5(random()::text || clock_timestamp()::text) from 1 for 16) NOT NULL, + hash_id VARCHAR(16) NOT NULL, domain VARCHAR(255) NOT NULL, batch_id integer, creation_time timestamp without time zone DEFAULT NOW() NOT NULL, @@ -191,9 +192,11 @@ sub create_new_test { $hash_id = $recent_hash_id; } else { + $hash_id = substr(md5_hex(time().rand()), 0, 16); $dbh->do( - "INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO test_results (hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?,?)", undef, + $hash_id, $batch_id, $priority, $queue_label, @@ -202,9 +205,6 @@ sub create_new_test { $domain, $undelegated, ); - - ( undef, $hash_id ) = $dbh->selectrow_array( - "SELECT id,hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint ); } return $hash_id; @@ -329,7 +329,7 @@ sub add_batch_job { $dbh->do( "DROP INDEX IF EXISTS test_results__progress" ); $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); - $dbh->do( "COPY test_results(domain ,batch_id, priority, queue, fingerprint, params, undelegated) 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; @@ -337,7 +337,8 @@ sub add_batch_job { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - $dbh->pg_putcopydata("$test_params->{domain}\t$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\t$undelegated\n"); + 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)" ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 6f6c4a563..dae71ef9b 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -59,7 +59,7 @@ sub create_db { $dbh->do( 'CREATE TABLE IF NOT EXISTS test_results ( id integer PRIMARY KEY AUTOINCREMENT, - hash_id VARCHAR(16) DEFAULT NULL, + hash_id VARCHAR(16) NOT NULL, domain VARCHAR(255) NOT NULL, batch_id integer NULL, creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, @@ -315,7 +315,8 @@ sub add_batch_job { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - $sth->execute( substr(md5_hex(time().rand()), 0, 16), $test_params->{domain}, $batch_id, $priority, $queue_label, $fingerprint, $encoded_params, $undelegated ); + 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__fingerprint ON test_results (fingerprint)" ); From 5df15c0e402cb049773bfdc45adcd8747c42fe85 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 18 Aug 2021 12:31:33 +0200 Subject: [PATCH 311/424] Refactor the search for pre-existing test to reuse New method to specifically perform the search for SQLite and MySQL. --- lib/Zonemaster/Backend/DB.pm | 1 + lib/Zonemaster/Backend/DB/MySQL.pm | 34 ++++++++++++++++------------- lib/Zonemaster/Backend/DB/SQLite.pm | 33 +++++++++++++++++----------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 1eea40e83..03f65db69 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -22,6 +22,7 @@ requires qw( get_test_history get_test_params process_unfinished_tests_give_up + recent_test_hash_id select_unfinished_tests test_progress test_results diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index d63ab0660..8489f14c3 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -165,6 +165,18 @@ sub create_db { ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); } +sub recent_test_hash_id { + my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; + + 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 $recent_hash_id; +} + sub create_new_batch_job { my ( $self, $username ) = @_; @@ -202,29 +214,23 @@ sub create_new_test { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - my $result_id; + my $hash_id; my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; eval { $dbh->do( q[LOCK TABLES test_results WRITE] ); - my ( $recent_hash_id ) = $dbh->selectrow_array( - q[ - SELECT hash_id FROM test_results WHERE fingerprint = ? AND (TO_SECONDS(NOW()) - TO_SECONDS(creation_time)) < ? - ], - undef, $fingerprint, $seconds_between_tests_with_same_params, - ); + + 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 - $result_id = $recent_hash_id; + $hash_id = $recent_hash_id; } else { $dbh->do( - q[ - INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?) - ], + "INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?,?,?,?,?,?,?)", undef, $batch_id, $priority, @@ -235,15 +241,13 @@ sub create_new_test { $undelegated, ); - my ( undef, $hash_id ) = $dbh->selectrow_array( + ( undef, $hash_id ) = $dbh->selectrow_array( "SELECT id, hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint); - - $result_id = $hash_id; } }; $dbh->do( q[UNLOCK TABLES] ); - return $result_id; + return $hash_id; } sub test_progress { diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 85b768bbc..12552eb92 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -123,6 +123,21 @@ sub create_db { return 1; } +# 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; + my ( $recent_hash_id ) = $dbh->selectrow_array( + "SELECT hash_id FROM test_results WHERE fingerprint = ? AND test_start_time > DATETIME('now', ?)", + undef, $fingerprint, "-$age_reuse_previous_test seconds" + ); + + return $recent_hash_id; +} + sub create_new_batch_job { my ( $self, $username ) = @_; @@ -159,30 +174,23 @@ sub create_new_test { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - my $result_id; + my $hash_id; my $priority = $test_params->{priority}; my $queue_label = $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 fingerprint = ? AND creation_time > DATETIME('now', ?)", - undef, - $fingerprint, - "-$seconds seconds" - ); + my $recent_hash_id = $self->recent_test_hash_id( $seconds, $fingerprint ); if ( $recent_hash_id ) { # A recent entry exists, so return its id - $result_id = $recent_hash_id; + $hash_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); + $hash_id = substr(md5_hex(time().rand()), 0, 16); my $fields = 'hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated'; $dbh->do( @@ -197,10 +205,9 @@ sub create_new_test { $test_params->{domain}, $undelegated, ); - $result_id = $hash_id; } - return $result_id; # Return test ID, either test previously run or just created. + return $hash_id; # Return test ID, either test previously run or just created. } sub test_progress { From 5753c36e0179410dc63144bd2d18d84f066641ef Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 16:36:20 +0200 Subject: [PATCH 312/424] Fix index key field for PostgreSQL --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 2cdd82211..4c3fcc1ca 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -106,7 +106,7 @@ sub create_db { } if ( not exists($indexes->{test_results__domain_undelegated}) ) { $dbh->do( - "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), (params->>'undelegated'))" + "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), undelegated)" ); } @@ -401,7 +401,7 @@ sub add_batch_job { $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 ((params->>'domain'), undelegated)" ); $dbh->commit(); } From e58e7dd57b32284b7df34e254081c08d2be9aa2e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 17:48:50 +0200 Subject: [PATCH 313/424] Remove MySQL trigger This trigger was used to generate the hash_id during the insertion of a new row. This is not needed anymore since we generate the hash_id before performing the insertion in database. --- lib/Zonemaster/Backend/DB/MySQL.pm | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 4bba14075..32c380e7b 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -85,27 +85,6 @@ sub create_db { ' ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'test_results' table", data => $dbh->errstr() ); - - # Manually create the trigger if it does not exist - # the clause IF NOT EXISTS is not available for MariaDB < 10.1.4 and MySQL - - # retrieve all triggers by name - my $triggers = $dbh->selectall_hashref( 'SHOW TRIGGERS', 'Trigger' ); - if ( not exists($triggers->{before_insert_test_results}) ) { - $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; - ' - ); - } - # Manually create the index if it does not exist # the clause IF NOT EXISTS is not available for MySQL (used with FreeBSD) From aee9b0406ddf55fbc394f8ed948cbac72f7da56b Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 18 Aug 2021 14:10:37 +0200 Subject: [PATCH 314/424] Apply same logic for test reuse to PostgreSQL --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 56 +++++++++++++++---------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index d4cfd5a02..ba5ade3a8 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -139,6 +139,18 @@ sub create_db { } +sub recent_test_hash_id { + my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; + + my $dbh = $self->dbh; + 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 $recent_hash_id; +} + sub test_progress { my ( $self, $test_id, $progress ) = @_; @@ -192,31 +204,33 @@ sub create_new_test { 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 $sth = $dbh->prepare( " - INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) - SELECT ?, ?, ?, ?, ?, ?, ? - WHERE NOT EXISTS ( - SELECT * FROM test_results - WHERE fingerprint = ? - AND creation_time > NOW() - ?::interval - )" ); - my $nb_inserted = $sth->execute( # - $batch_id, - $priority, - $queue_label, - $fingerprint, - $encoded_params, - $domain, - $undelegated, - $fingerprint, - sprintf( "%d seconds", $seconds_between_tests_with_same_params ), - ); + my $recent_hash_id = $self->recent_test_hash_id( $seconds_between_tests_with_same_params, $fingerprint ); - my ( undef, $hash_id ) = $dbh->selectrow_array( - "SELECT id,hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint ); + if ( $recent_hash_id ) { + # A recent entry exists, so return its id + $hash_id = $recent_hash_id; + } + else { + $dbh->do( + "INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) VALUES (?, ?, ?, ?, ?, ?, ?)", + undef, + $batch_id, + $priority, + $queue_label, + $fingerprint, + $encoded_params, + $domain, + $undelegated, + ); + + ( undef, $hash_id ) = $dbh->selectrow_array( + "SELECT id,hash_id FROM test_results WHERE fingerprint=? ORDER BY id DESC LIMIT 1", undef, $fingerprint ); + } return $hash_id; } From 5c32a004e94bd6906f99855c637befa1c0f849be Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 17 Aug 2021 16:07:20 +0200 Subject: [PATCH 315/424] Add "user_name" and "api_key" columns (PostgreSQL) Update the patch script to populate these new columns. --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 9 ++++++++- ...patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 9a85b017d..337e9b237 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -131,6 +131,8 @@ sub create_db { $dbh->do( 'CREATE TABLE IF NOT EXISTS users ( id serial PRIMARY KEY, + username VARCHAR(128), + api_key VARCHAR(512), user_info json ) ' @@ -151,7 +153,12 @@ sub add_api_user_to_db { my ( $self, $user_name, $api_key ) = @_; 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 } ) ); + my $nb_inserted = $dbh->do( "INSERT INTO users (user_info, username, api_key) VALUES (?,?,?)", + undef, + encode_json( { username => $user_name, api_key => $api_key } ), + $user_name, + $api_key + ); return $nb_inserted; } diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 82098d4c2..cb32b9ad7 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -79,6 +79,16 @@ sub patch_db { $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 + $dbh->do( "UPDATE users SET username = (user_info->>'username'), api_key = (user_info->>'api_key')" ); } patch_db(); From c11b043252555dc3919934fa4266eef589d6adfa Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 16 Aug 2021 15:44:16 +0200 Subject: [PATCH 316/424] Add new column "domain" (PostgreSQL) Standardize columns across database engines. --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 4c3fcc1ca..9a85b017d 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -63,6 +63,7 @@ sub create_db { 'CREATE TABLE IF NOT EXISTS test_results ( id serial PRIMARY KEY, hash_id VARCHAR(16) DEFAULT substring(md5(random()::text || clock_timestamp()::text) from 1 for 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, @@ -106,7 +107,7 @@ sub create_db { } if ( not exists($indexes->{test_results__domain_undelegated}) ) { $dbh->do( - "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), undelegated)" + "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); } @@ -223,8 +224,8 @@ sub create_new_test { my $queue_label = $test_params->{queue}; my $sth = $dbh->prepare( " - INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, undelegated) - SELECT ?, ?, ?, ?, ?, ? + INSERT INTO test_results (batch_id, priority, queue, fingerprint, params, domain, undelegated) + SELECT ?, ?, ?, ?, ?, ?, ? WHERE NOT EXISTS ( SELECT * FROM test_results WHERE fingerprint = ? @@ -236,6 +237,7 @@ sub create_new_test { $queue_label, $fingerprint, $encoded_params, + $domain, $undelegated, $fingerprint, sprintf( "%d seconds", $seconds_between_tests_with_same_params ), @@ -332,7 +334,7 @@ sub get_test_history { 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 + WHERE domain=" . $dbh->quote( $p->{frontend_params}->{domain} ) . " $undelegated ORDER BY id DESC OFFSET $p->{offset} LIMIT $p->{limit}"; my $sth1 = $dbh->prepare( $query ); @@ -385,7 +387,7 @@ sub add_batch_job { $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, fingerprint, params, undelegated) FROM STDIN" ); + $dbh->do( "COPY test_results(domain ,batch_id, priority, queue, fingerprint, params, undelegated) FROM STDIN" ); foreach my $domain ( @{$params->{domains}} ) { $test_params->{domain} = $domain; @@ -393,7 +395,7 @@ sub add_batch_job { my $encoded_params = $self->encode_params( $test_params ); my $undelegated = $self->undelegated ( $test_params ); - $dbh->pg_putcopydata("$batch_id\t$priority\t$queue_label\t$fingerprint\t$encoded_params\t$undelegated\n"); + $dbh->pg_putcopydata("$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)" ); @@ -401,7 +403,7 @@ sub add_batch_job { $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'), undelegated)" ); + $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); $dbh->commit(); } From 63f2afb02f1266d09eefb5062e7cb2bc951fb3dd Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 16:38:42 +0200 Subject: [PATCH 317/424] Apply correction into patch script --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 303a9d1e8..bde107d04 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -46,6 +46,14 @@ sub patch_db { }; print( "Error while changing DB schema: " . $@ ) if ($@); + # Update index + eval { + # clause IF EXISTS available since PostgreSQL >= 9.2 + $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); + $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), undelegated)" ); + }; + print( "Error while updating the index: " . $@ ) if ($@); + # Update the "undelegated" column my $sth1 = $dbh->prepare('SELECT id, params from test_results', undef); $sth1->execute; From 29e7eb2de4ec59f0b20a5470f706e82e28925518 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 10 Aug 2021 12:00:04 +0200 Subject: [PATCH 318/424] Apply DRY principle on "schedule_for_retry" --- lib/Zonemaster/Backend/DB.pm | 13 +++++++++++++ lib/Zonemaster/Backend/DB/MySQL.pm | 6 ------ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 6 ------ lib/Zonemaster/Backend/DB/SQLite.pm | 6 ------ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index c5fb962d3..7f98805db 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -216,6 +216,19 @@ sub process_dead_test { } } +=head2 schedule_for_retry + +For the test with the given "hash_id" increments its number of retries by 1, +resets its progress to 0 and its start time to NULL. + +=cut + +sub schedule_for_retry { + my ( $self, $hash_id ) = @_; + + $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); +} + # A thin wrapper around DBI->connect to ensure similar behavior across database # engines. sub _new_dbh { diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 29b5230a9..2d6bd27b0 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -510,12 +510,6 @@ 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 { - my ( $self, $hash_id ) = @_; - - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); -} - sub get_relative_start_time { my ( $self, $hash_id ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 5217dc7a7..2cdd82211 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -453,12 +453,6 @@ 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 { - my ( $self, $hash_id ) = @_; - - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); -} - sub get_relative_start_time { my ( $self, $hash_id ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index e3fe04570..70a9af3a6 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -444,12 +444,6 @@ 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 { - my ( $self, $hash_id ) = @_; - - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $hash_id); -} - sub get_relative_start_time { my ( $self, $hash_id ) = @_; From 36c5190dd8e4725d8b3ac8b24bad848820b9b19a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 17:56:33 +0200 Subject: [PATCH 319/424] Update patch scripts (MySQL, PostgreSQL) * column "hash_id" NOT NULL (MySQL) * drop the MySQL trigger * drop the default value for the hash_id field (PostgreSQL) --- share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 8 ++++++++ share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 2 ++ 2 files changed, 10 insertions(+) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index 43f40b6c6..6e5fa81fc 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -16,6 +16,14 @@ 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) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 42e52f39a..7b1fc6324 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -17,6 +17,8 @@ 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 { From ebe380adfd2484e02ad25454cda264de13dc08c3 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 18 Aug 2021 14:12:01 +0200 Subject: [PATCH 320/424] Move "get_test_params" method to DB.pm --- lib/Zonemaster/Backend/DB.pm | 27 +++++++++++++++++++++++-- lib/Zonemaster/Backend/DB/MySQL.pm | 20 ------------------ lib/Zonemaster/Backend/DB/PostgreSQL.pm | 19 ----------------- lib/Zonemaster/Backend/DB/SQLite.pm | 20 ------------------ 4 files changed, 25 insertions(+), 61 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 03f65db69..735ae434e 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -6,12 +6,13 @@ use Moose::Role; use 5.14.2; -use JSON::PP; use Digest::MD5 qw(md5_hex); use Encode; +use JSON::PP; use Log::Any qw( $log ); use Zonemaster::Engine::Profile; +use Zonemaster::Backend::Errors; requires qw( add_batch_job @@ -20,7 +21,6 @@ requires qw( create_new_test from_config get_test_history - get_test_params process_unfinished_tests_give_up recent_test_hash_id select_unfinished_tests @@ -180,6 +180,29 @@ 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 ) = @_; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 8489f14c3..55792109c 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -5,7 +5,6 @@ our $VERSION = '1.1.0'; use Moose; use 5.14.2; -use Data::Dumper; use DBI qw(:utils); use JSON::PP; @@ -268,25 +267,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 ); - - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $params_json; - - my $result; - eval { - $result = decode_json( $params_json ); - }; - - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; - - return decode_json( $params_json ); -} - sub test_results { my ( $self, $test_id, $new_results ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index ba5ade3a8..56d1aac6e 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -235,25 +235,6 @@ sub create_new_test { 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 ); - - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $params_json; - - eval { $result = decode_json( encode_utf8( $params_json ) ); }; - - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; - - return $result; -} - sub test_results { my ( $self, $test_id, $results ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 12552eb92..f42ad6481 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -5,7 +5,6 @@ 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 JSON::PP; @@ -228,25 +227,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 ); - - die Zonemaster::Backend::Error::ResourceNotFound->new( message => "Test not found", data => { test_id => $test_id } ) - unless defined $params_json; - - my $result; - eval { - $result = decode_json( $params_json ); - }; - - die Zonemaster::Backend::Error::JsonError->new( reason => "$@", data => { test_id => $test_id } ) - if $@; - - return $result; -} - sub test_results { my ( $self, $test_id, $new_results ) = @_; From 15fdf25968341d9e9d8f83dfcc95f951a60bff5d Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 17 Aug 2021 16:17:22 +0200 Subject: [PATCH 321/424] Remove the "user_info" column from table "users" Update the patch scripts to drop this column for all database engine. --- lib/Zonemaster/Backend/DB/MySQL.pm | 6 ++---- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 10 ++++------ lib/Zonemaster/Backend/DB/SQLite.pm | 6 ++---- ...h_mysql_db_zonemaster_backend_ver_7.0.0.pl | 8 ++++++++ ...tgresql_db_zonemaster_backend_ver_7.0.0.pl | 3 +++ ..._sqlite_db_zonemaster_backend_ver_7.0.0.pl | 19 +++++++++++++++++++ t/test01.t | 2 +- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 2d6bd27b0..6b023067f 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -159,8 +159,7 @@ sub create_db { 'CREATE TABLE IF NOT EXISTS users ( id integer AUTO_INCREMENT primary key, username varchar(128), - api_key varchar(512), - user_info blob DEFAULT NULL + api_key varchar(512) ) ENGINE=InnoDB; ' ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); @@ -178,9 +177,8 @@ 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 (?,?,?)", + "INSERT INTO users (username, api_key) VALUES (?,?)", undef, - 'NULL', $user_name, $api_key, ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 337e9b237..f219bc44f 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -132,8 +132,7 @@ sub create_db { 'CREATE TABLE IF NOT EXISTS users ( id serial PRIMARY KEY, username VARCHAR(128), - api_key VARCHAR(512), - user_info json + api_key VARCHAR(512) ) ' ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL error, could not create 'users' table", data => $dbh->errstr() ); @@ -144,7 +143,7 @@ sub user_exists_in_db { my ( $self, $user ) = @_; my $dbh = $self->dbh; - my ( $id ) = $dbh->selectrow_array( "SELECT id FROM users WHERE user_info->>'username'=?", undef, $user ); + my ( $id ) = $dbh->selectrow_array( "SELECT id FROM users WHERE username=?", undef, $user ); return $id; } @@ -153,9 +152,8 @@ sub add_api_user_to_db { my ( $self, $user_name, $api_key ) = @_; my $dbh = $self->dbh; - my $nb_inserted = $dbh->do( "INSERT INTO users (user_info, username, api_key) VALUES (?,?,?)", + my $nb_inserted = $dbh->do( "INSERT INTO users (username, api_key) VALUES (?,?)", undef, - encode_json( { username => $user_name, api_key => $api_key } ), $user_name, $api_key ); @@ -168,7 +166,7 @@ sub user_authorized { my $dbh = $self->dbh; my $id = - $dbh->selectrow_array( "SELECT id FROM users WHERE user_info->>'username'=? AND user_info->>'api_key'=?", + $dbh->selectrow_array( "SELECT id FROM users WHERE username=? AND api_key=?", undef, $user, $api_key ); return $id; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 70a9af3a6..ff0b838ad 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -115,8 +115,7 @@ sub create_db { 'CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username varchar(128), - api_key varchar(512), - user_info json DEFAULT NULL + api_key varchar(512) ) ' ) or die Zonemaster::Backend::Error::Internal->new( reason => "SQLite error, could not create 'users' table", data => $dbh->errstr() ); @@ -136,9 +135,8 @@ 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 (?,?,?)", + "INSERT INTO users (username, api_key) VALUES (?,?)", undef, - 'NULL', $user_name, $api_key, ); diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl index bf3ce6c79..43f40b6c6 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl @@ -49,6 +49,14 @@ sub patch_db { $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 ($@); } patch_db(); diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index cb32b9ad7..42e52f39a 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -89,6 +89,9 @@ sub patch_db { # update the columns $dbh->do( "UPDATE users SET username = (user_info->>'username'), api_key = (user_info->>'api_key')" ); + + # remove the "user_info" column from the "users" table + $dbh->do( "ALTER TABLE users DROP COLUMN IF EXISTS user_info" ); } patch_db(); diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl index 8bb737e73..fb4b675bb 100644 --- a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl @@ -52,6 +52,25 @@ sub patch_db { $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/t/test01.t b/t/test01.t index 013c527e6..7cb813563 100644 --- a/t/test01.t +++ b/t/test01.t @@ -77,7 +77,7 @@ is( $engine->add_api_user( { username => "zonemaster_test", api_key => "zonemast my $user_check_query; if ( $db_backend eq 'PostgreSQL' ) { - $user_check_query = q/SELECT * FROM users WHERE user_info->>'username' = 'zonemaster_test'/; + $user_check_query = q/SELECT * FROM users WHERE username = 'zonemaster_test'/; } elsif ( $db_backend eq 'MySQL' || $db_backend eq 'SQLite' ) { $user_check_query = q/SELECT * FROM users WHERE username = 'zonemaster_test'/; From a97e57b170d679d6dfcb6b399a851cc3bff459ef Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 16 Aug 2021 15:49:18 +0200 Subject: [PATCH 322/424] Update patch script to create and populate column Need to add the new column with a default value and then remove this default value to avoid error on column creation. --- ...patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index bde107d04..82098d4c2 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -40,8 +40,9 @@ sub patch_db { print( "Error while changing DB schema: " . $@ ) if ($@); - # Add missing "undelegated" column + # 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 ($@); @@ -50,10 +51,15 @@ sub patch_db { eval { # clause IF EXISTS available since PostgreSQL >= 9.2 $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); - $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results ((params->>'domain'), undelegated)" ); + $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); }; print( "Error while updating 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; From 3b4a1c54de290b88a65daa258a3e03c0684b9f07 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 18:22:10 +0200 Subject: [PATCH 323/424] Remove database lock (MySQL) --- lib/Zonemaster/Backend/DB/MySQL.pm | 45 +++++++++++++----------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 32c380e7b..98b00185a 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -172,32 +172,27 @@ sub create_new_test { my $priority = $test_params->{priority}; my $queue_label = $test_params->{queue}; - eval { - $dbh->do( q[LOCK TABLES test_results WRITE] ); - - my $recent_hash_id = $self->recent_test_hash_id( $seconds_between_tests_with_same_params, $fingerprint ); + 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, - ); - } - }; - $dbh->do( q[UNLOCK TABLES] ); + 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; } From 13043c81afaea033b0d03c7fdc0884c621a04f61 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 18 Aug 2021 14:47:17 +0200 Subject: [PATCH 324/424] Move "create_new_batch_job" method to DB.pm --- lib/Zonemaster/Backend/DB.pm | 30 +++++++++++++++++++++++- lib/Zonemaster/Backend/DB/MySQL.pm | 31 ++++--------------------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 30 ++++-------------------- lib/Zonemaster/Backend/DB/SQLite.pm | 30 ++++-------------------- 4 files changed, 44 insertions(+), 77 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 735ae434e..e59b25538 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -17,10 +17,10 @@ use Zonemaster::Backend::Errors; requires qw( add_batch_job create_db - create_new_batch_job create_new_test from_config get_test_history + last_insert_id process_unfinished_tests_give_up recent_test_hash_id select_unfinished_tests @@ -114,6 +114,34 @@ sub add_api_user { return $result; } +# 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 ) = $self->last_insert_id( $dbh, "batch_jobs" ); + + return $new_batch_id; +} + # Standard SQL, can be here sub user_exists_in_db { my ( $self, $user ) = @_; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 55792109c..01d3e06b0 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -164,6 +164,11 @@ sub create_db { ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); } +sub last_insert_id { + my ( $self, $dbh ) = @_; + return $dbh->{mysql_insertid}; +} + sub recent_test_hash_id { my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; @@ -176,32 +181,6 @@ sub recent_test_hash_id { return $recent_hash_id; } -sub create_new_batch_job { - my ( $self, $username ) = @_; - - my ( $batch_id, $creation_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 Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) - 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 ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 56d1aac6e..e67463c44 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -139,6 +139,11 @@ sub create_db { } +sub last_insert_id { + my ( $self, $dbh, $table ) = @_; + return $dbh->last_insert_id( undef, undef, $table, undef); +} + sub recent_test_hash_id { my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; @@ -169,31 +174,6 @@ sub test_progress { 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 Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) - 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; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index f42ad6481..cf2c8cd83 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -122,6 +122,11 @@ sub create_db { return 1; } +sub last_insert_id { + my ( $self, $dbh, $table ) = @_; + return $dbh->sqlite_last_insert_rowid; +} + # 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. @@ -137,31 +142,6 @@ sub recent_test_hash_id { return $recent_hash_id; } -sub create_new_batch_job { - my ( $self, $username ) = @_; - - my ( $batch_id, $creation_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 Zonemaster::Backend::Error::Conflict->new( message => 'Batch job still running', data => { batch_id => $batch_id, creation_time => $creation_time } ) - 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 ) = @_; From 2a9c23d1718d92a71a2713c877e526a1527d5bee Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 17 Aug 2021 16:26:34 +0200 Subject: [PATCH 325/424] Remove duplications by using standard SQL --- lib/Zonemaster/Backend/DB.pm | 47 +++++++++++++++++++++++-- lib/Zonemaster/Backend/DB/MySQL.pm | 30 ---------------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 33 ----------------- lib/Zonemaster/Backend/DB/SQLite.pm | 30 ---------------- t/test01.t | 8 +---- 5 files changed, 45 insertions(+), 103 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 7f98805db..1eea40e83 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -14,7 +14,6 @@ use Log::Any qw( $log ); use Zonemaster::Engine::Profile; requires qw( - add_api_user_to_db add_batch_job create_db create_new_batch_job @@ -26,8 +25,6 @@ requires qw( select_unfinished_tests test_progress test_results - user_authorized - user_exists_in_db get_relative_start_time ); @@ -116,6 +113,50 @@ sub add_api_user { return $result; } +# 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, $queue_label ) = @_; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 6b023067f..d63ab0660 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -165,36 +165,6 @@ sub create_db { ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); } -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 (username, api_key) VALUES (?,?)", - undef, - $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 ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index f219bc44f..d4cfd5a02 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -139,39 +139,6 @@ sub create_db { } -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; -} - -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; -} - -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; -} - sub test_progress { my ( $self, $test_id, $progress ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index ff0b838ad..85b768bbc 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -123,36 +123,6 @@ sub create_db { 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 (username, api_key) VALUES (?,?)", - undef, - $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 ) = @_; diff --git a/t/test01.t b/t/test01.t index 7cb813563..b8ad5c420 100644 --- a/t/test01.t +++ b/t/test01.t @@ -75,13 +75,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 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 From e91ed0536624edf4dcb26bc1aeb82b52aa4cf6bd Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 18:23:31 +0200 Subject: [PATCH 326/424] Move "create_new_test" method to DB.pm --- lib/Zonemaster/Backend/DB.pm | 43 +++++++++++++++++++++- lib/Zonemaster/Backend/DB/MySQL.pm | 41 --------------------- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 40 --------------------- lib/Zonemaster/Backend/DB/SQLite.pm | 47 ------------------------- 4 files changed, 42 insertions(+), 129 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index e58374ed0..4af14950b 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -17,7 +17,6 @@ use Zonemaster::Backend::Errors; requires qw( add_batch_job create_db - create_new_test from_config get_test_history process_unfinished_tests_give_up @@ -113,6 +112,48 @@ sub add_api_user { 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 ) = @_; diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 98b00185a..bdfc7179c 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -156,47 +156,6 @@ sub recent_test_hash_id { return $recent_hash_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 $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; -} - sub test_progress { my ( $self, $test_id, $progress ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 2066e12cc..8f9eca960 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -170,46 +170,6 @@ sub test_progress { return $result; } -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, - $domain, - $undelegated, - ); - } - - return $hash_id; -} - sub test_results { my ( $self, $test_id, $results ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index dae71ef9b..ef0d06f90 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -137,53 +137,6 @@ sub recent_test_hash_id { return $recent_hash_id; } -sub create_new_test { - my ( $self, $domain, $test_params, $seconds, $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, $fingerprint ); - - if ( $recent_hash_id ) { - # A recent entry exists, so return its id - $hash_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. - $hash_id = substr(md5_hex(time().rand()), 0, 16); - - my $fields = 'hash_id, batch_id, priority, queue, fingerprint, params, domain, undelegated'; - $dbh->do( - "INSERT INTO test_results ($fields) VALUES (?,?,?,?,?,?,?,?)", - undef, - $hash_id, - $batch_id, - $priority, - $queue_label, - $fingerprint, - $encoded_params, - $test_params->{domain}, - $undelegated, - ); - } - - return $hash_id; # Return test ID, either test previously run or just created. -} - sub test_progress { my ( $self, $test_id, $progress ) = @_; From 5f8d6b5e04cbb92f85ba3f7d94bb9687d2e006ee Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 18 Aug 2021 15:55:47 +0200 Subject: [PATCH 327/424] Use generic DBI "last_insert_id" https://metacpan.org/pod/DBI#last_insert_id --- lib/Zonemaster/Backend/DB.pm | 3 +-- lib/Zonemaster/Backend/DB/MySQL.pm | 5 ----- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 5 ----- lib/Zonemaster/Backend/DB/SQLite.pm | 5 ----- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index e59b25538..e58374ed0 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -20,7 +20,6 @@ requires qw( create_new_test from_config get_test_history - last_insert_id process_unfinished_tests_give_up recent_test_hash_id select_unfinished_tests @@ -137,7 +136,7 @@ sub create_new_batch_job { if ( $batch_id ); $dbh->do( "INSERT INTO batch_jobs (username) VALUES (?)", undef, $username ); - my ( $new_batch_id ) = $self->last_insert_id( $dbh, "batch_jobs" ); + my $new_batch_id = $dbh->last_insert_id( undef, undef, "batch_jobs", undef ); return $new_batch_id; } diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index 01d3e06b0..94aedc4c9 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -164,11 +164,6 @@ sub create_db { ) or die Zonemaster::Backend::Error::Internal->new( reason => "MySQL error, could not create 'users' table", data => $dbh->errstr() ); } -sub last_insert_id { - my ( $self, $dbh ) = @_; - return $dbh->{mysql_insertid}; -} - sub recent_test_hash_id { my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e67463c44..b556890c1 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -139,11 +139,6 @@ sub create_db { } -sub last_insert_id { - my ( $self, $dbh, $table ) = @_; - return $dbh->last_insert_id( undef, undef, $table, undef); -} - sub recent_test_hash_id { my ( $self, $age_reuse_previous_test, $fingerprint ) = @_; diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index cf2c8cd83..6f6c4a563 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -122,11 +122,6 @@ sub create_db { return 1; } -sub last_insert_id { - my ( $self, $dbh, $table ) = @_; - return $dbh->sqlite_last_insert_rowid; -} - # 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. From 33e3feb1e91e965ab6771e0fa86dae22c1cd04d5 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 19 Aug 2021 19:03:10 +0200 Subject: [PATCH 328/424] Remove unnecessary module --- lib/Zonemaster/Backend/DB/SQLite.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index ef0d06f90..36d6e3404 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -8,7 +8,6 @@ use 5.14.2; use DBI qw(:utils); use Digest::MD5 qw(md5_hex); use JSON::PP; -use Log::Any qw( $log ); use Zonemaster::Backend::Errors; From 321bd69e9bb3af213490992d9f02ce1831d34c48 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 14:32:49 +0200 Subject: [PATCH 329/424] Undocuments experimental feature "maximal_number_of_retries" --- docs/Configuration.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 6f61ad99d..67aa0cab5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -32,7 +32,6 @@ * [PUBLIC PROFILES and PRIVATE PROFILES sections](#PUBLIC-PROFILES-and-PRIVATE-PROFILES-sections) * [ZONEMASTER section](#ZONEMASTER-section) * [max_zonemaster_execution_time](#max_zonemaster_execution_time) - * [maximal_number_of_retries](#maximal_number_of_retries) * [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) @@ -347,7 +346,6 @@ 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 @@ -357,17 +355,6 @@ 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 - -A non-negative integer. Max length 5 digits. - -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 strictly positive integer. Max length 5 digits. From 90e4858eb7e71d55ef647dc6f108c216ec8c98f6 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 14:35:19 +0200 Subject: [PATCH 330/424] Removes experimental feature based on key "maximal_number_of_retries" --- lib/Zonemaster/Backend/Config.pm | 14 ----------- lib/Zonemaster/Backend/DB.pm | 31 ++++--------------------- lib/Zonemaster/Backend/DB/MySQL.pm | 10 +++----- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 10 +++----- lib/Zonemaster/Backend/DB/SQLite.pm | 10 +++----- script/zonemaster_backend_testagent | 3 +-- 6 files changed, 14 insertions(+), 64 deletions(-) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 870332103..ed612b08b 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -140,7 +140,6 @@ sub parse { $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' ); @@ -262,9 +261,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 ); } @@ -558,14 +554,6 @@ L. - -Returns a number. - - =head2 ZONEMASTER_age_reuse_previous_test Get the value of @@ -610,7 +598,6 @@ sub LANGUAGE_locale { return %{ $_[0]->{_LAN 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}; } @@ -633,7 +620,6 @@ UNITCHECK { _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_maximal_number_of_retries', '_ZONEMASTER_maximal_number_of_retries', \&untaint_non_negative_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 ); diff --git a/lib/Zonemaster/Backend/DB.pm b/lib/Zonemaster/Backend/DB.pm index 4af14950b..3898ab8cd 100644 --- a/lib/Zonemaster/Backend/DB.pm +++ b/lib/Zonemaster/Backend/DB.pm @@ -302,21 +302,15 @@ sub get_batch_job_result { } sub process_unfinished_tests { - my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; my $sth1 = $self->select_unfinished_tests( # $queue_label, $test_run_timeout, - $test_run_max_retries, ); while ( my $h = $sth1->fetchrow_hashref ) { - if ( $h->{nb_retries} < $test_run_max_retries ) { - $self->schedule_for_retry($h->{hash_id}); - } - else { - $self->force_end_test($h->{hash_id}, $h->{results}, $test_run_timeout); - } + $self->force_end_test($h->{hash_id}, $h->{results}, $test_run_timeout); } } @@ -340,26 +334,9 @@ sub force_end_test { } sub process_dead_test { - my ( $self, $hash_id, $test_run_max_retries ) = @_; - my ( $nb_retries, $results ) = $self->dbh->selectrow_array("SELECT nb_retries, results FROM test_results WHERE hash_id = ?", undef, $hash_id); - if ( $nb_retries < $test_run_max_retries) { - $self->schedule_for_retry($hash_id); - } else { - $self->force_end_test($hash_id, $results, $self->get_relative_start_time($hash_id)); - } -} - -=head2 schedule_for_retry - -For the test with the given "hash_id" increments its number of retries by 1, -resets its progress to 0 and its start time to NULL. - -=cut - -sub schedule_for_retry { my ( $self, $hash_id ) = @_; - - $self->dbh->do("UPDATE test_results SET nb_retries = nb_retries + 1, progress = 0, test_start_time = NULL WHERE hash_id=?", undef, $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 diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index bdfc7179c..ddd9e7679 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -330,35 +330,31 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; 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( # $test_run_timeout, - $test_run_max_retries, $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( # $test_run_timeout, - $test_run_max_retries, ); return $sth; } diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 8f9eca960..113308883 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -318,35 +318,31 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; 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", $test_run_timeout ), - $test_run_max_retries, $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", $test_run_timeout ), - $test_run_max_retries, ); return $sth; } diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 36d6e3404..89db4e17e 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -287,35 +287,31 @@ sub add_batch_job { } sub select_unfinished_tests { - my ( $self, $queue_label, $test_run_timeout, $test_run_max_retries ) = @_; + my ( $self, $queue_label, $test_run_timeout ) = @_; 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", $test_run_timeout ), - $test_run_max_retries, $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", $test_run_timeout ), - $test_run_max_retries, ); return $sth; } diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index 53893d409..323536e6f 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -143,7 +143,6 @@ sub main { $self->db->process_unfinished_tests( $self->config->ZONEMASTER_lock_on_queue, $self->config->ZONEMASTER_max_zonemaster_execution_time, - $self->config->ZONEMASTER_maximal_number_of_retries, ); if ( $id ) { $log->info( "Test found: $id" ); @@ -153,7 +152,7 @@ sub main { if ( $@ ) { chomp $@; $log->error( "Test died: $id: $@" ); - $self->db->process_dead_test( $id, $self->config->ZONEMASTER_maximal_number_of_retries ) + $self->db->process_dead_test( $id ) } else { $log->info( "Test completed: $id" ); From df162a812f996a5198efba4cdd3d641ed2ed70c4 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 14:37:00 +0200 Subject: [PATCH 331/424] Updates to remove handling of removed feature "maximal_number_of_retries" --- .../maintenance/Garbage-Collection-Testing.md | 78 +------------------ 1 file changed, 4 insertions(+), 74 deletions(-) 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) - - ``` From cf712004316aeddf6ca9bf412cd9dffeccbcecf7 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 14:37:36 +0200 Subject: [PATCH 332/424] Removes references to removed feature "maximal_number_of_retries" --- share/backend_config.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index 66dbe8444..6765956a2 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -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" From 0ae0e9919452790078ef6c3ebacd0427378ba107 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 14:38:12 +0200 Subject: [PATCH 333/424] Removes tests based on "maximal_number_of_retries" --- t/config.t | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/t/config.t b/t/config.t index 17a18d4de..62c410f71 100644 --- a/t/config.t +++ b/t/config.t @@ -56,7 +56,6 @@ subtest 'Everything but NoWarnings' => sub { 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 ); @@ -86,7 +85,6 @@ subtest 'Everything but NoWarnings' => sub { }, 'set: PRIVATE PROFILES'; 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'; @@ -109,7 +107,6 @@ subtest 'Everything but NoWarnings' => sub { 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'; @@ -416,18 +413,6 @@ subtest 'Everything but NoWarnings' => sub { } qr{ZONEMASTER\.max_zonemaster_execution_time.*0}, 'die: Invalid ZONEMASTER.max_zonemaster_execution_time value'; - throws_ok { - my $text = q{ - [DB] - engine = SQLite - - [ZONEMASTER] - maximal_number_of_retries = -1 - }; - Zonemaster::Backend::Config->parse( $text ); - } - qr{ZONEMASTER\.maximal_number_of_retries.*-1}, 'die: Invalid ZONEMASTER.maximal_number_of_retries value'; - throws_ok { my $text = q{ [DB] From dcfd7ac46a222ed52fa43ca2b27d76bf609a6497 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 30 Sep 2021 20:45:12 +0200 Subject: [PATCH 334/424] Updates installation instructions * Unifies the headline structure * Simplifies the instructions by shell commands for creation of database and database user * Corrects instructions for FreeBSD not to use sudo --- docs/Installation.md | 291 ++++++++++++++++++++----------------------- 1 file changed, 134 insertions(+), 157 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index cb72b48ef..b0c3aa8b8 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -1,6 +1,6 @@ # Installation -**Table of contents** +## Table of contents * [1. Overview](#1-overview) * [2. Prerequisites](#2-prerequisites) @@ -26,10 +26,18 @@ * [6.1 Smoke test](#61-smoke-test) * [6.2 What to do next?](#62-what-to-do-next) * [7. Upgrade Zonemaster database](#7-upgrade-zonemaster-database) -* [Appendices](#appendices) - * [A. Installation with MariaDB](#a-installation-with-mariadb) - * [B. Installation with PostgreSQL](#b-installation-with-postgresql) - * [C. Cleaning up the database](#c-cleaning-up-the-database) +* [8. Installation with MariaDB](#8-installation-with-mariadb) + * [8.1 MariaDB (CentOS)](#81-mariadb-centos) + * [8.2. MariaDB (Debian/Ubuntu)](#82-mariadb-debianubuntu) + * [8.3. MySQL (FreeBSD)](#83-mysql-freebsd) +* [9. Installation with PostgreSQL](#9-installation-with-postgresql) + * [9.1. PostgreSQL (CentOS)](#91-postgresql-centos) + * [9.2. PostgreSQL (Debian/Ubuntu)](#92-postgresql-debianubuntu) + * [9.3. PostgreSQL (FreeBSD)](#93-postgresql-freebsd) +* [10. Cleaning up the database](#10-cleaning-up-the-database) + * [10.1. MariaDB and MySQL](#101-mariadb-and-mysql) + * [10.2. PostgreSQL](#102-postgresql) + * [10.3. SQLite](#103-sqlite) ## 1. Overview @@ -116,9 +124,9 @@ 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][Upgrade -database] first. If you instead want to start from afresh, then go to [appendix -C][Cleaning database] and remove the old database first. +If you upgrade and want to keep the database, go to section +["Upgrade Zonemaster database"] first. If you instead want to start from afresh, +then go to section ["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. @@ -141,13 +149,13 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster #### 3.2.2 Instructions for other engines (CentOS) -See appendices for [MariaDB][MariaDB instructions on CentOS] and -[PostgreSQL][PostgreSQL instructions on CentOS]. +See sections for [MariaDB][MariaDB instructions CentOS] and +[PostgreSQL][PostgreSQL instructions CentOS]. ### 3.3 Database configuration (CentOS) -Finally initialize the database: +Created the database tables: ```sh sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl @@ -253,9 +261,10 @@ 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][Upgrade -database] first. If you instead want to start from afresh, then go to [appendix -C][Cleaning database] and remove the old database first. +If you upgrade and want to keep the database, go to section 7 +["Upgrade Zonemaster database"] first. If you instead want to start from afresh, +then go to section ["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. @@ -278,13 +287,13 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster #### 4.2.2 Instructions for other engines (Debian/Ubuntu) -See appendices for [MariaDB][MariaDB instructions on Debian] and -[PostgreSQL][PostgreSQL instructions on Debian]. +See sections for [MariaDB][MariaDB instructions Debian] and +[PostgreSQL][PostgreSQL instructions Debian]. ### 4.3 Database configuration (Debian/Ubuntu) -Finally initialize the database: +Created the database tables: ```sh sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl @@ -370,9 +379,10 @@ 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][Upgrade -database] first. If you instead want to start from afresh, then go to [appendix -C][Cleaning database] and remove the old database first. +If you upgrade and want to keep the database, go to section +["Upgrade Zonemaster database"] first. If you instead want to start from afresh, +then go to section ["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. @@ -399,13 +409,13 @@ install -v -m 755 -o zonemaster -g zonemaster -d /var/db/zonemaster #### 5.2.2 Instructions for other engines (FreeBSD) -See appendices for [MariaDB][MariaDB instructions on FreeBSD] and -[PostgreSQL][PostgreSQL instructions on FreeBSD]. +See sections for [MariaDB][MariaDB instructions FreeBSD] and +[PostgreSQL][PostgreSQL instructions FreeBSD]. ### 5.3 Database configuration (FreeBSD) -Finally initialize the database: +Created the database tables: ```sh su -m zonemaster -c "`perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir(qw(Zonemaster-Backend))'`/create_db.pl" @@ -430,7 +440,7 @@ service zm_testagent start ### 5.5 Post-installation (FreeBSD) -To check the running daemons run: +To check that the running daemons run: ```sh service zm_rpcapi status @@ -471,17 +481,12 @@ database to use it with the new version of Zonemaster-Backend. Please see the [upgrade][README.md-upgrade] information. +## 8. Installation with MariaDB -## Appendices +First follow the installation instructions for the OS in question, and then go +to this section to install MariaDB. - -### A. Installation with MariaDB - -* [CentOS](#a1-mariadb-installation-on-centos) -* [Debian/Ubuntu](#a2-mariadb-installation-on-debian-and-ubuntu) -* [FreeBSD](#a3-mysql-installation-on-freebsd) - -#### A.1. MariaDB installation on CentOS +### 8.1. MariaDB (CentOS) Configure Zonemaster::Backend to use the correct database engine: @@ -499,35 +504,22 @@ sudo systemctl enable mariadb sudo systemctl start mariadb ``` -To initialize the database (unless you keep an old database) connect to the -MariaDB server: +To create the database and the database user (unless you keep an old database) +run the command. Edit the script first if you want a non-default user name or +password. ```sh -sudo mysql -``` - -Create the database: -```sql -CREATE DATABASE zonemaster; +sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" ``` -Create a new user and give it all permissions on the newly created database: -```sql -CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; -GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; -``` - -Logout from database: +Update the `/etc/zonemaster/backend_config.ini` file with username and password +if non-default values are used. -```sql -exit; -``` +Now go back to "[Database configuration](#33-database-configuration-centos)" +to create the database tables and then continue with the steps after that. -Then update the `/etc/zonemaster/backend_config.ini` file with username and -password unless the default values are used. - -#### A.2. MariaDB installation on Debian and Ubuntu +### 8.2. MariaDB (Debian/Ubuntu) Configure Zonemaster::Backend to use the correct database engine: @@ -543,37 +535,24 @@ Install the database engine and its dependencies: sudo apt install mariadb-server libdbd-mysql-perl ``` -To initialize the database (unless you keep an old database) connect to the -MariaDB server: +To create the database and the database user (unless you keep an old database) +run the command. Edit the script first if you want a non-default user name or +password. ```sh -sudo mysql +sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" ``` -Create the database: -```sql -CREATE DATABASE zonemaster; -``` +Update the `/etc/zonemaster/backend_config.ini` file with username and password +if non-default values are used. -Create a new user and give it all permissions on the newly created database: -```sql -CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; -GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; -``` - -Logout from database: - -```sql -exit; -``` - -Then update the `/etc/zonemaster/backend_config.ini` file with username and -password unless the default values are used. +Now go back to "[Database configuration](#43-database-configuration-debianubuntu)" +to create the database tables and then continue with the steps after that. -#### A.3. MySQL installation on FreeBSD +### 8.3. MySQL (FreeBSD) -> MySQL is used on FreeBSD +> MariaDB is not compatible with Zonemaster on FreeBSD. MySQL is used instead. Configure Zonemaster::Backend to use the correct database engine: @@ -590,55 +569,42 @@ sysrc mysql_enable="YES" service mysql-server start ``` -Read the current root password for MySQL: +Read the current root password for MySQL (unless it has been changed +already). ```sh cat /root/.mysql_secret ``` -Connect to MySQL interactively: +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 -mysql -u root -h localhost -p +/usr/local/bin/mysqladmin -u root -p password '' ``` -Reset root password in MySQL (required by MySQL). Replace -`` with the password from the file above -(or another one of your choice): - -```sql -ALTER USER 'root'@'localhost' IDENTIFIED BY ''; -``` - -Unless you keep an old database, initialize the database: -```sql -CREATE DATABASE zonemaster; -``` +To create the database and the database user (unless you keep an old database) +run the command. Edit the script first if you want a non-default user name or +password. Use the MySQL root password when prompted. -Create a new user and give it all permissions on the newly created database: -```sql -CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster'; -GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost'; -``` - -Logout from database: - -```sql -exit; +```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';" ``` -Then update the `/etc/zonemaster/backend_config.ini` file with username and -password unless the default values are used. +Update the `/etc/zonemaster/backend_config.ini` file with 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. -### B. Installation with PostgreSQL -* [CentOS](#b1-postgresql-installation-on-centos) -* [Debian/Ubuntu](#b2-postgresql-installation-on-debian-and-ubuntu) -* [FreeBSD](#b3-postgresql-installation-on-freebsd) +## 9. Installation with PostgreSQL +First follow the installation instructions for the OS in question, and then go +to this section to install PostgreSQL. -#### B.1. PostgreSQL installation on CentOS +### 9.1. PostgreSQL (CentOS) Configure Zonemaster::Backend to use the correct database engine: @@ -671,20 +637,21 @@ Install, configure and start database engine: sudo systemctl start postgresql ``` -Initialize Zonemaster database and user (unless you keep an old database): +To create the database and the database user (unless you keep an old database) +run the command on one line. ```sh -sudo -u postgres createuser -P zonemaster +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" ``` -This will ask for a password that should be copied in `backend_config.ini`. +Update the `/etc/zonemaster/backend_config.ini` file with username and password +if non-default values are used. -```sh -sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster -``` +Now go back to "[Database configuration](#33-database-configuration-centos)" +to create the database tables and then continue with the steps after that. -#### B.2. PostgreSQL installation on Debian and Ubuntu +### 9.2. PostgreSQL (Debian/Ubuntu) Configure Zonemaster::Backend to use the correct database engine: @@ -700,20 +667,22 @@ sudo apt install postgresql libdbd-pg-perl > **Note:** See the [backend configuration] documentation for details. -Initialize Zonemaster database and user (unless you keep an old database): +To create the database and the database user (unless you keep an old database) +run the command on one line. ```sh -sudo -u postgres createuser -P zonemaster +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" + ``` -This will ask for a password that should be copied in `backend_config.ini`. +Update the `/etc/zonemaster/backend_config.ini` file with username and password +if non-default values are used. -```sh -sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster -``` +Now go back to "[Database configuration](#43-database-configuration-debianubuntu)" +to create the database tables and then continue with the steps after that. -#### B.3. PostgreSQL installation on FreeBSD +### 9.3. PostgreSQL (FreeBSD) Configure Zonemaster::Backend to use the correct database engine: @@ -731,20 +700,21 @@ service postgresql initdb service postgresql start ``` -Initialize Zonemaster database and user (unless you keep an old database): +To create the database and the database user (unless you keep an old database) +run the command on one line. ```sh -sudo -u postgres createuser -P zonemaster +psql -U postgres -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" ``` -This will ask for a password that should be copied in `backend_config.ini`. +Update the `/etc/zonemaster/backend_config.ini` file with username and password +if non-default values are used. -```sh -sudo -u postgres createdb -O zonemaster -E UTF8 zonemaster -``` +Now go back to "[Database configuration](#53-database-configuration-freebsd)" +to create the database tables and then continue with the steps after that. -### C. Cleaning up the database +## 10. 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` @@ -752,42 +722,49 @@ 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). -#### C.1. MySQL +### 10.1. MariaDB and MySQL ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -mysql --user=root --password < ./cleanup-mysql.sql +mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql ``` -#### C.2. PostgreSQL +### 10.2. PostgreSQL + +CentOS, Debian and Ubuntu: ```sh -cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` -sudo -u postgres psql -f ./cleanup-postgres.sql # MUST BE VERIFIED! +sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/cleanup-postgres.sql # MUST BE VERIFIED! +``` + +FreeBSD (as root): + +```sh +psql -U postgres -f `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-postgres.sql ``` -#### C.3. SQLite +### 10.3. SQLite Remove the database file and recreate it following the installation instructions above. ------- -[Backend configuration]: Configuration.md -[Cleaning database]: #c-cleaning-up-the-database -[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 on CentOS]: #a1-mariadb-installation-on-centos -[MariaDB instructions on Debian]: #a2-mariadb-installation-on-debian-and-ubuntu -[MariaDB instructions on FreeBSD]: #a3-mysql-installation-on-freebsd -[PostgreSQL instructions on CentOS]: #b1-postgresql-installation-on-centos -[PostgreSQL instructions on Debian]: #b2-postgresql-installation-on-debian-and-ubuntu -[PostgreSQL instructions on FreeBSD]: #b3-postgresql-installation-on-freebsd -[Post-installation]: #6-post-installation -[README.md-upgrade]: /README.md#upgrade -[Upgrade database]: #7-upgrade-zonemaster-database -[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 +["Cleaning up the database"]: #10-cleaning-up-the-database +["Upgrade Zonemaster database"]: #7-upgrade-zonemaster-database +[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 CentOS]: #81-mariadb-centos +[MariaDB instructions Debian]: #82-mariadb-debianubuntu +[MariaDB instructions FreeBSD]: #83-mysql-freebsd +[Post-installation]: #6-post-installation +[PostgreSQL instructions CentOS]: #91-postgresql-centos +[PostgreSQL instructions Debian]: #92-postgresql-debianubuntu +[PostgreSQL instructions FreeBSD]: #93-postgresql-freebsd +[README.md-upgrade]: README.md#upgrade +[Upgrade database]: #7-upgrade-zonemaster-database +[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 From 7f6f1e84b33a06fae8b9b834de53863dae1914bf Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 1 Oct 2021 14:39:17 +0200 Subject: [PATCH 335/424] Empty From ae4dd66ced7ce400eaf3642e2978b620c376178b Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 4 Oct 2021 16:29:39 +0200 Subject: [PATCH 336/424] Adds binary perl-doc to Debian/Ubuntu installation required by zmb --- docs/Installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Installation.md b/docs/Installation.md index cb72b48ef..5adf2a92c 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -204,7 +204,7 @@ 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 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 perl-doc starman ``` > **Note**: libio-stringy-perl is listed here even though it's not a direct From 408fc9ad934b5585980c0b8b3728613b98a33166 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 4 Oct 2021 18:38:54 +0200 Subject: [PATCH 337/424] Editorial updates --- docs/Installation.md | 63 +++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index b0c3aa8b8..10dfa8fc0 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -125,8 +125,8 @@ 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 -["Upgrade Zonemaster database"] first. If you instead want to start from afresh, -then go to section ["Cleaning up the database"] and remove the old database first. +"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, +then go to section "[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. @@ -155,7 +155,7 @@ See sections for [MariaDB][MariaDB instructions CentOS] and ### 3.3 Database configuration (CentOS) -Created the database tables: +Create the database tables: ```sh sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl @@ -262,8 +262,8 @@ 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 -["Upgrade Zonemaster database"] first. If you instead want to start from afresh, -then go to section ["Cleaning up the database"] and remove the old database +"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, +then go to section "[Cleaning up the database]" and remove the old database first. If you keep the database, skip the initialization of the Zonemaster database, @@ -293,7 +293,7 @@ See sections for [MariaDB][MariaDB instructions Debian] and ### 4.3 Database configuration (Debian/Ubuntu) -Created the database tables: +Create the database tables: ```sh sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')/create_db.pl @@ -380,8 +380,8 @@ 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 -["Upgrade Zonemaster database"] first. If you instead want to start from afresh, -then go to section ["Cleaning up the database"] and remove the old database +"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, +then go to section "[Cleaning up the database]" and remove the old database first. If you keep the database, skip the initialization of the Zonemaster database, @@ -415,7 +415,7 @@ See sections for [MariaDB][MariaDB instructions FreeBSD] and ### 5.3 Database configuration (FreeBSD) -Created the database tables: +Create the database tables: ```sh su -m zonemaster -c "`perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir(qw(Zonemaster-Backend))'`/create_db.pl" @@ -505,15 +505,15 @@ sudo systemctl start mariadb ``` To create the database and the database user (unless you keep an old database) -run the command. Edit the script first if you want a non-default user name or -password. +run the command. Edit the command first if you want a non-default database name, +user name or password. ```sh sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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-centos)" to create the database tables and then continue with the steps after that. @@ -536,15 +536,15 @@ sudo apt install mariadb-server libdbd-mysql-perl ``` To create the database and the database user (unless you keep an old database) -run the command. Edit the script first if you want a non-default user name or -password. +run the command. Edit the command first if you want a non-default database name, +user name or password. ```sh sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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](#43-database-configuration-debianubuntu)" to create the database tables and then continue with the steps after that. @@ -585,15 +585,15 @@ when prompted for that. ``` To create the database and the database user (unless you keep an old database) -run the command. Edit the script first if you want a non-default user name or -password. Use the MySQL root password when prompted. +run the command. Edit command first if you want a non-default database name, +user name or password. 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 `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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](#53-database-configuration-freebsd)" to create the database tables and then continue with the steps after that. @@ -641,7 +641,7 @@ To create the database and the database user (unless you keep an old database) run the command on one line. ```sh -sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` Update the `/etc/zonemaster/backend_config.ini` file with username and password @@ -671,7 +671,7 @@ To create the database and the database user (unless you keep an old database) run the command on one line. ```sh -sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` @@ -704,7 +704,8 @@ To create the database and the database user (unless you keep an old database) run the command on one line. ```sh -psql -U postgres -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH ENCODING 'UTF8';" +psql -U postgres -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" +psql -U postgres -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` Update the `/etc/zonemaster/backend_config.ini` file with username and password @@ -724,6 +725,14 @@ database (obviously taking all data with it). ### 10.1. MariaDB and MySQL +CentOS, Debian and Ubuntu: + +```sh +sudo mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql +``` + +FreeBSD (you will get prompted for MySQL password): + ```sh mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql ``` @@ -733,7 +742,7 @@ mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_di CentOS, Debian and Ubuntu: ```sh -sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/cleanup-postgres.sql # MUST BE VERIFIED! +sudo -u postgres psql -f $(perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")')/cleanup-postgres.sql ``` FreeBSD (as root): @@ -748,9 +757,8 @@ Remove the database file and recreate it following the installation instructions ------- -["Cleaning up the database"]: #10-cleaning-up-the-database -["Upgrade Zonemaster database"]: #7-upgrade-zonemaster-database [Backend configuration]: Configuration.md +[Cleaning up the database]: #10-cleaning-up-the-database [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 @@ -762,6 +770,7 @@ Remove the database file and recreate it following the installation instructions [PostgreSQL instructions Debian]: #92-postgresql-debianubuntu [PostgreSQL instructions FreeBSD]: #93-postgresql-freebsd [README.md-upgrade]: README.md#upgrade +[Upgrade Zonemaster database]: #7-upgrade-zonemaster-database [Upgrade database]: #7-upgrade-zonemaster-database [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 From 826f1d1d692406d863fd522cadb0d1af094dca95 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 6 Oct 2021 10:25:33 +0200 Subject: [PATCH 338/424] Editorial updates --- docs/Installation.md | 59 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 10dfa8fc0..257142864 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -424,16 +424,11 @@ su -m zonemaster -c "`perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir( ### 5.4 Service startup (FreeBSD) -Enable services at startup: +Enable services at startup and start service: ```sh sysrc zm_rpcapi_enable="YES" sysrc zm_testagent_enable="YES" -``` - -Start services: - -```sh service zm_rpcapi start service zm_testagent start ``` @@ -504,9 +499,9 @@ sudo systemctl enable mariadb sudo systemctl start mariadb ``` -To create the database and the database user (unless you keep an old database) -run the command. Edit the command first if you want a non-default database name, -user name or 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. ```sh sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" @@ -535,9 +530,9 @@ Install the database engine and its dependencies: sudo apt install mariadb-server libdbd-mysql-perl ``` -To create the database and the database user (unless you keep an old database) -run the command. Edit the command first if you want a non-default database name, -user name or 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. ```sh sudo mysql -e "CREATE DATABASE zonemaster;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" @@ -564,7 +559,7 @@ sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= MySQL/' /usr/local/etc/zonemaster/back Install, configure and start database engine (and Perl bindings): ```sh -pkg install mysql57-server p5-DBD-mysql +pkg install -y mysql57-server p5-DBD-mysql sysrc mysql_enable="YES" service mysql-server start ``` @@ -584,9 +579,10 @@ when prompted for that. /usr/local/bin/mysqladmin -u root -p password '' ``` -To create the database and the database user (unless you keep an old database) -run the command. Edit command first if you want a non-default database name, -user name or password. Use the MySQL root password when prompted. +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';" @@ -637,15 +633,16 @@ Install, configure and start database engine: sudo systemctl start postgresql ``` -To create the database and the database user (unless you keep an old database) -run the command on one line. +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. ```sh sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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-centos)" to create the database tables and then continue with the steps after that. @@ -667,16 +664,17 @@ 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) -run the command on one line. +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. ```sh sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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](#43-database-configuration-debianubuntu)" to create the database tables and then continue with the steps after that. @@ -694,22 +692,23 @@ sed -i '' '/[[:<:]]engine[[:>:]]/ s/=.*/= PostgreSQL/' /usr/local/etc/zonemaster Install, configure and start database engine (and Perl bindings): ```sh -pkg install postgresql12-server p5-DBD-Pg +pkg install -y postgresql12-server p5-DBD-Pg sysrc postgresql_enable="YES" service postgresql initdb service postgresql start ``` -To create the database and the database user (unless you keep an old database) -run the command on one line. +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';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with username and password -if non-default values are used. +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](#53-database-configuration-freebsd)" to create the database tables and then continue with the steps after that. @@ -728,7 +727,7 @@ database (obviously taking all data with it). CentOS, Debian and Ubuntu: ```sh -sudo mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql +sudo mysql --user=root < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql ``` FreeBSD (you will get prompted for MySQL password): From 19a3920490ca5f5351c31276b243eed46e66ead0 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 6 Oct 2021 12:03:01 +0200 Subject: [PATCH 339/424] Editorial updates --- docs/Installation.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 257142864..b7acd8a7f 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -500,11 +500,17 @@ sudo systemctl start mariadb ``` 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. +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;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" +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, @@ -531,11 +537,17 @@ sudo apt install mariadb-server libdbd-mysql-perl ``` 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. +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;" -e "CREATE USER 'zonemaster'@'localhost' IDENTIFIED BY 'zonemaster';" -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" +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 @@ -635,10 +647,13 @@ Install, configure and start database engine: 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. +password. To be safe run the commands one by one. ```sh -sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" +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';" ``` Update the `/etc/zonemaster/backend_config.ini` file with database name, username @@ -666,10 +681,14 @@ sudo apt install postgresql libdbd-pg-perl 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. +password. To be safe run the commands one by one. + +```sh +sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" +``` ```sh -sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" +sudo -u postgres psql -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` From 4fbf3c42d175ba97edd4dee89a1fd8ddc40a2e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 7 Oct 2021 10:08:48 +0200 Subject: [PATCH 340/424] refactor validate_params --- lib/Zonemaster/Backend/RPCAPI.pm | 12 +++++------- t/parameters_validation.t | 24 ++++++++++++------------ t/test_validate_syntax.t | 13 +++++++------ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 0bf355f93..2bf6e656c 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -507,6 +507,7 @@ sub get_test_results { $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, @@ -668,8 +669,8 @@ sub jsonrpc_validate { } }; } - - my @error_response = $self->validate_params($jsonrpc_request->{method}, $jsonrpc_request->{params}); + 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 { @@ -688,9 +689,7 @@ sub jsonrpc_validate { } sub validate_params { - my ( $self, $method, $params ) = @_; - my $method_schema = $json_schemas{$method}; - + my ( $self, $method_schema, $sub_validator, $params ) = @_; my @error_response = (); if (blessed $method_schema) { @@ -733,8 +732,7 @@ sub validate_params { } # Add messages from extra validation function - if ( $extra_validators{$method} ) { - my $sub_validator = $extra_validators{$method}; + if ( defined $sub_validator ) { push @error_response, $self->$sub_validator($params); } return @error_response; diff --git a/t/parameters_validation.t b/t/parameters_validation.t index b9deaab54..862b08c96 100644 --- a/t/parameters_validation.t +++ b/t/parameters_validation.t @@ -31,12 +31,12 @@ my $rpcapi = Zonemaster::Backend::RPCAPI->new( ); sub test_validation { - my ( $method, $test_cases ) = @_; + my ( $method_name, $method_schema, $sub_validator, $test_cases ) = @_; - subtest "Method $method" => sub { + subtest "Method $method_name" => sub { for my $test_case (@$test_cases) { subtest 'Test case: ' . $test_case->{name} => sub { - my @res = $rpcapi->validate_params( $method, $test_case->{input}); + 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); }; } @@ -44,11 +44,11 @@ sub test_validation { } subtest 'Test JSON schema' => sub { - local $Zonemaster::Backend::RPCAPI::json_schemas{test_joi} = joi->new->object->strict->props( + my $test_joi_schema = joi->new->object->strict->props( hostname => joi->new->string->max(10)->required ); - local $Zonemaster::Backend::RPCAPI::json_schemas{test_raw_schema} = { + my $test_raw_schema = { type => 'object', additionalProperties => 0, required => [ 'hostname' ], @@ -88,12 +88,12 @@ subtest 'Test JSON schema' => sub { } ]; - test_validation 'test_joi', $test_cases; - test_validation 'test_raw_schema', $test_cases; + 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 { - local $Zonemaster::Backend::RPCAPI::json_schemas{test_custom_error} = { + my $test_custom_error_schema = { type => 'object', additionalProperties => 0, required => [ 'hostname' ], @@ -150,11 +150,11 @@ subtest 'Test custom error message' => sub { } ]; - test_validation 'test_custom_error', $test_cases; + test_validation 'test_custom_error', $test_custom_error_schema, undef, $test_cases; }; subtest 'Test extra validators' => sub { - local $Zonemaster::Backend::RPCAPI::extra_validators{test_extra_validator} = sub { + my $test_extra_validator_sub = sub { my ($self, $input) = @_; my @errors; if ( $input->{answer} != 42 ) { @@ -164,7 +164,7 @@ subtest 'Test extra validators' => sub { return @errors; }; - local $Zonemaster::Backend::RPCAPI::json_schemas{test_extra_validator} = { + my $test_extra_validator_schema = { type => 'object', properties => { answer => { @@ -193,5 +193,5 @@ subtest 'Test extra validators' => sub { } ]; - test_validation 'test_extra_validator', $test_cases; + test_validation 'test_extra_validator', $test_extra_validator_schema, $test_extra_validator_sub, $test_cases; }; diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 09c4fc459..4f8de6673 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -31,6 +31,7 @@ my $engine = Zonemaster::Backend::RPCAPI->new( ); 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 { @@ -225,30 +226,30 @@ subtest 'Everything but NoWarnings' => sub { $frontend_params->{ds_info}->[0]->{digtype} = 1; $frontend_params->{ds_info}->[0]->{keytag} = 5000; - is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 0, encode_utf8( 'Valid Algorithm Type [numeric format]' ) ) + 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", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 'a'; $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; - is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid Algorithm Type' ) ) + 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", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{algorithm} = 1; $frontend_params->{ds_info}->[0]->{digest} = '01234567890123456789012345678901234567890'; - is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest length' ) ) + 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", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{digest} = 'Z123456789012345678901234567890123456789'; - is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest format' ) ) + 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", $frontend_params ) ); $frontend_params->{ds_info}->[0]->{digest} = '0123456789012345678901234567890123456789'; $frontend_params->{ds_info}->[0]->{digtype} = -1; - is( scalar $engine->validate_params( "start_domain_test", $frontend_params ), 1, encode_utf8( 'Invalid digest type' ) ) + 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", $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", $frontend_params ), 1, encode_utf8( 'Invalid keytag' ) ) + 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", $frontend_params ) ); }; From d85afa6c0688f4f33771404461686319f0da2fe1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 12 Oct 2021 15:33:14 +0200 Subject: [PATCH 341/424] Raise an error if method called from remote IP The "add_api_user" method should be called from a local IP. --- lib/Zonemaster/Backend/RPCAPI.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 76e20add2..687149de1 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -575,6 +575,12 @@ 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 => 'Unauthorized to call this method from a remote IP', + data => { remote_ip => $remote_ip } + ); + } }; if ($@) { handle_exception( $@ ); From dd83a686e0971277c0130833eebd1bebc9035205 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 12 Oct 2021 15:37:22 +0200 Subject: [PATCH 342/424] Update documentation with example output --- docs/API.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index d4281efe2..622aa32ae 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1208,9 +1208,15 @@ Omitting params: Trying to add a user over non-localhost: ```json { - "result": 0, "id": 1, - "jsonrpc": "2.0" + "jsonrpc": "2.0", + "error": { + "code": -32603, + "data": { + "remote_ip": "10.0.0.1" + }, + "message": "Unauthorized to call this method from a remote IP" + } } ``` From b03c8590f319cfd7c0e0bc1f529c73f52521604e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 09:20:59 +0200 Subject: [PATCH 343/424] Improve error wording --- docs/API.md | 2 +- lib/Zonemaster/Backend/RPCAPI.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index 622aa32ae..2b7750100 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1215,7 +1215,7 @@ Trying to add a user over non-localhost: "data": { "remote_ip": "10.0.0.1" }, - "message": "Unauthorized to call this method from a remote IP" + "message": "Call to \"add_api_user\" method not permitted from a remote IP" } } ``` diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 687149de1..5f3c178a9 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -577,7 +577,7 @@ sub add_api_user { } else { die Zonemaster::Backend::Error::PermissionDenied->new( - message => 'Unauthorized to call this method from a remote IP', + message => 'Call to "add_api_user" method not permitted from a remote IP', data => { remote_ip => $remote_ip } ); } From c98cc6c3c8d2e0671f42737bb3839bb71f9bd90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 14 Oct 2021 12:17:48 +0200 Subject: [PATCH 344/424] add error message translation --- MANIFEST | 4 + MANIFEST.SKIP | 3 + Makefile.PL | 23 ++++++ docs/API.md | 10 ++- docs/Configuration.md | 7 +- lib/Zonemaster/Backend/Config.pm | 11 +++ lib/Zonemaster/Backend/RPCAPI.pm | 120 ++++++++++++++++++++-------- lib/Zonemaster/Backend/Validator.pm | 15 ++-- share/GNUmakefile | 44 ++++++++++ share/Makefile | 14 ++++ share/backend_config.ini | 3 +- share/fr.po | 83 +++++++++++++++++++ t/test01.t | 3 + t/test_validate_syntax.t | 28 +++++-- 14 files changed, 319 insertions(+), 49 deletions(-) create mode 100644 share/GNUmakefile create mode 100644 share/Makefile create mode 100644 share/fr.po diff --git a/MANIFEST b/MANIFEST index 666fad413..f8739e94e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -22,6 +22,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 @@ -66,6 +67,9 @@ share/zm-rpcapi.lsb share/zm-testagent.lsb share/zm_rpcapi-bsd share/zm_testagent-bsd +share/locale/fr/LC_MESSAGES/Zonemaster-Backend.mo +share/Makefile +share/GNUmakefile t/config.t t/db.t t/parameters_validation.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index e48ce15e3..70f616c01 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -8,6 +8,9 @@ ^\.github/pull_request_template\.md$ ^\.travis\.yml$ ^docs/internal-documentation/ +\.po$ +^share/[^/]*\.mo$ +^share/Zonemaster-Backend.pot$ #!start included /usr/share/perl/5.20/ExtUtils/MANIFEST.SKIP # Avoid version control files. diff --git a/Makefile.PL b/Makefile.PL index f737f5f3f..f5e02acd6 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'; @@ -53,4 +54,26 @@ install_script 'zmb'; no_index directory => 'CodeSnippets'; no_index directory => 'Doc'; +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) touch-po all' . "\n"; + } else { + $text = 'pure_all :: share/Makefile' . "\n" + . "\t" . 'cd share && $(MAKE) touch-po all' . "\n"; + }; + return $text; +}; + +install_share; + WriteAll; diff --git a/docs/API.md b/docs/API.md index d4281efe2..b8678a0a2 100644 --- a/docs/API.md +++ b/docs/API.md @@ -683,9 +683,12 @@ Example response: #### `"params"` -An object with the property: +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, the default value is defined in the [language section] + of the configuration. #### `"result"` @@ -782,6 +785,9 @@ An object with the following properties: * `"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, the default value is defined in the [language section] + of the configuration. > TODO: Clarify the purpose of each `"params"` property. > @@ -1477,6 +1483,7 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [JSON-RPC 2.0]: https://www.jsonrpc.org/specification [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag +[language section]: Configuration.md#usage [Name server]: #name-server [Privilege levels]: #privilege-levels [Profile name]: #profile-name @@ -1490,4 +1497,3 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [`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 6f61ad99d..e589d825b 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -271,6 +271,11 @@ English is the Zonemaster default language, but it can be blocked from being allowed by RPC-API by including some `locale tag` in the configuration, but none starting with language code for English ("en"). +The first language in the list will be used as the default for the RPC API +error messages. If translation not available, then the error messages will be +send untranslated, i.e. in English. See the [API documentation] to know which +methods support error message localization. + #### Out-of-the-box support The default installation and configuration supports the @@ -411,7 +416,7 @@ 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 diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 870332103..0d910001d 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -286,8 +286,13 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { $obj->_reset_LANGUAGE_locale(); + my $default_set = 0; for my $locale_tag ( split / +/, $value ) { $obj->_add_LANGUAGE_locale( $locale_tag ); + if ( not $default_set ) { + $obj->_set_LANGUAGE_default_locale( $locale_tag ); + $default_set = 1; + } } } } @@ -507,6 +512,10 @@ E.g.: }, ) +=head2 LANGUAGE_default_locale +Get the first locale passed to the L in the configuration file. +Returns a string. + =head2 PUBLIC_PROFILES @@ -607,6 +616,7 @@ sub POSTGRESQL_password { return $_[0]->{_POSTGR sub POSTGRESQL_database { return $_[0]->{_POSTGRESQL_database}; } sub SQLITE_database_file { return $_[0]->{_SQLITE_database_file}; } sub LANGUAGE_locale { return %{ $_[0]->{_LANGUAGE_locale} }; } +sub LANGUAGE_default_locale { return $_[0]->{_LANGUAGE_default_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}; } @@ -621,6 +631,7 @@ sub RPCAPI_enable_add_batch_job { return $_[0]->{_RPCAPI # Compile time generation of setters for the properties documented above UNITCHECK { _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_strictly_positive_millis ); + _create_setter( '_set_LANGUAGE_default_locale', '_LANGUAGE_default_locale', \&untaint_locale_tag ); _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 ); diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 76e20add2..4f17aa743 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -16,6 +16,9 @@ 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[setlocale LC_MESSAGES]; +use Encode; # Zonemaster Modules use Zonemaster::LDNS; @@ -185,7 +188,8 @@ $json_schemas{get_data_from_parent_zone} = { additionalProperties => 0, required => [ 'domain' ], properties => { - domain => $zm_validator->domain_name + domain => $zm_validator->domain_name, + language => $zm_validator->language_tag, } }; sub get_data_from_parent_zone { @@ -231,7 +235,7 @@ sub _check_domain { my ( $self, $domain ) = @_; if ( !defined( $domain ) ) { - return ( $domain, { status => 'nok', message => 'Domain name required' } ); + return ( $domain, { status => 'nok', message => N__ 'Domain name required' } ); } if ( $domain =~ m/[^[:ascii:]]+/ ) { @@ -242,7 +246,7 @@ sub _check_domain { $domain, { status => 'nok', - message => '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' } ); } @@ -252,7 +256,7 @@ sub _check_domain { $domain, { status => 'nok', - message => 'The domain name contains non-ascii characters and IDNA is not installed' + message => N__ 'The domain name contains non-ascii characters and IDNA is not installed' } ); } @@ -263,7 +267,7 @@ sub _check_domain { $domain, { status => 'nok', - message => 'The domain name character(s) are not supported' + message => N__ 'The domain name character(s) are not supported' } ); } @@ -273,7 +277,7 @@ 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 => 'The domain 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' } ); @@ -289,7 +293,7 @@ $extra_validators{start_domain_test} = sub { $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 => 'Unknown profile' }; + push @errors, { path => '/profile', message => N__ 'Unknown profile' }; } } @@ -303,7 +307,7 @@ $extra_validators{start_domain_test} = sub { 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' ); - push @errors, { path => "/nameservers/$index/ip", message => 'Invalid IP address' } + 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} ) ); @@ -342,6 +346,7 @@ $json_schemas{start_domain_test} = { config => joi->string->compile, priority => $zm_validator->priority->compile, queue => $zm_validator->queue->compile, + language => $zm_validator->language_tag, } }; @@ -416,27 +421,8 @@ $json_schemas{get_test_results} = { sub get_test_results { my ( $self, $params ) = @_; - my $language = $params->{language}; - - my %locales = $self->{config}->LANGUAGE_locale; - - my $locale; - if ( length $language == 2 ) { - if ( !exists $locales{$language} ) { - die "Undefined language string: '$language'\n"; - } - elsif ( scalar keys %{ $locales{$language} } > 1 ) { - die "Language string not unique: '$language'\n"; - } - ( $locale ) = keys %{ $locales{$language} }; - } - else { - if ( !exists $locales{substr $language, 0, 2}{$language} ) { - die "Undefined language string: '$language'\n"; - } - $locale = $language; - } - $locale .= '.UTF-8'; + # Already validated by json_validate + my ($locale, undef) = $self->_get_locale($params); my $result; my $translator; @@ -650,6 +636,68 @@ sub get_batch_job_result { 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'; + } else { + $locale = $self->{config}->LANGUAGE_default_locale + } + + 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}; + + $locale //= $self->{config}->LANGUAGE_default_locale; + + $ENV{LANGUAGE} = $locale; + setlocale( LC_MESSAGES, $locale ); + return @error_response; +} + my $rpc_request = joi->object->props( jsonrpc => joi->string->required, method => $zm_validator->jsonrpc_method()->required); @@ -658,6 +706,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, @@ -678,7 +727,7 @@ sub jsonrpc_validate { id => $jsonrpc_request->{id}, error => { code => '-32602', - message => "Missing 'params' object", + message => decode_utf8(__ "Missing 'params' object"), } }; } @@ -691,7 +740,7 @@ sub jsonrpc_validate { id => $jsonrpc_request->{id}, error => { code => '-32602', - message => 'Invalid method parameter(s).', + message => decode_utf8(__ 'Invalid method parameter(s).'), data => \@error_response } }; @@ -705,6 +754,8 @@ 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; } @@ -717,7 +768,7 @@ sub validate_params { # Handle 'required' errors globally so it does not get overwritten if ($details[1] eq 'required') { - $message = 'Missing property'; + $message = decode_utf8(__ 'Missing property'); } else { my @path = split '/', $err->path, -1; shift @path; # first item is an empty string @@ -742,12 +793,17 @@ sub validate_params { } 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; } diff --git a/lib/Zonemaster/Backend/Validator.pm b/lib/Zonemaster/Backend/Validator.pm index 5c8693871..d5595b947 100644 --- a/lib/Zonemaster/Backend/Validator.pm +++ b/lib/Zonemaster/Backend/Validator.pm @@ -10,6 +10,7 @@ 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( @@ -132,7 +133,7 @@ sub domain_name { return { type => 'string', pattern => $RELAXED_DOMAIN_NAME_RE, - 'x-error-message' => 'The domain name contains a character or characters not supported' + 'x-error-message' => N__ 'The domain name contains a character or characters not supported' }; } sub ds_info { @@ -144,22 +145,22 @@ sub ds_info { digest => { type => 'string', pattern => $DIGEST_RE, - 'x-error-message' => 'Invalid digest format' + 'x-error-message' => N__ 'Invalid digest format' }, algorithm => { type => 'number', minimum => 0, - 'x-error-message' => 'Algorithm must be a positive integer' + 'x-error-message' => N__ 'Algorithm must be a positive integer' }, digtype => { type => 'number', minimum => 0, - 'x-error-message' => 'Digest type must be a positive integer' + 'x-error-message' => N__ 'Digest type must be a positive integer' }, keytag => { type => 'number', minimum => 0, - 'x-error-message' => 'Keytag must be a positive integer' + 'x-error-message' => N__ 'Keytag must be a positive integer' } } }; @@ -168,7 +169,7 @@ sub ip_address { return { type => 'string', pattern => $IPADDR_RE, - 'x-error-message' => 'Invalid IP address', + 'x-error-message' => N__ 'Invalid IP address', }; } sub nameserver { @@ -200,7 +201,7 @@ sub language_tag { return { type => 'string', pattern => $LANGUAGE_RE, - 'x-error-message' => 'Invalid language tag format' + 'x-error-message' => N__ 'Invalid language tag format' }; } sub username { diff --git a/share/GNUmakefile b/share/GNUmakefile new file mode 100644 index 000000000..64d362930 --- /dev/null +++ b/share/GNUmakefile @@ -0,0 +1,44 @@ +.POSIX: +.SUFFIXES: .po .mo +.PHONY: all check-msg-args dist extract-pot tidy-po show-fuzzy touch-po update-po + +POFILES := $(shell find . -maxdepth 1 -type f -name '*.po') +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 + +touch-po: + @touch $(POTFILE) $(POFILES) + +update-po: extract-pot $(POFILES) + +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 + +$(POFILES): $(POTFILE) + @msgmerge --update --backup=none --quiet --no-location $(MSGMERGE_OPTS) $@ $(POTFILE) + +.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 66dbe8444..d250e9a2e 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -39,7 +39,8 @@ database_file = /var/lib/zonemaster/db.sqlite #enable_add_batch_job = no [LANGUAGE] -locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE +# The first language in this list will be used as default for error message translation +locale = en_US da_DK fi_FI fr_FR nb_NO sv_SE [PUBLIC PROFILES] #example_profile_1=/example/directory/test1_profile.json diff --git a/share/fr.po b/share/fr.po new file mode 100644 index 000000000..b8a04715f --- /dev/null +++ b/share/fr.po @@ -0,0 +1,83 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-14 11:25+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \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/t/test01.t b/t/test01.t index b8ad5c420..80e12c3b9 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 diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 4f8de6673..314f2aeef 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -21,6 +21,9 @@ engine = SQLite [SQLITE] database_file = $tempdir/zonemaster.sqlite + +[LANGUAGE] +locale = en_US fr_FR da_DK fi_FI nb_NO sv_SE EOF my $engine = Zonemaster::Backend::RPCAPI->new( @@ -227,29 +230,42 @@ subtest 'Everything but NoWarnings' => sub { $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", $frontend_params ) ); + 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( 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", $frontend_params ) ); + 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( 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", $frontend_params ) ); + or diag( encode_json$engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); $frontend_params->{ds_info}->[0]->{digest} = 'Z123456789012345678901234567890123456789'; 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", $frontend_params ) ); + 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", $frontend_params ) ); + 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", $frontend_params ) ); + 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; + $frontend_params->{language} = "zz"; + cmp_ok( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), '>', 0, encode_utf8( 'Invalid language, "zz" unknown' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + + $frontend_params->{language} = "fr-FR"; + cmp_ok( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), '>', 0, encode_utf8( 'Invalid language, should be underscore not hyphen' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + + $frontend_params->{language} = "nb_NO"; + is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 0, encode_utf8( 'Valid language' ) ) + or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); }; From d341439101e15812115f80941ddda9eaaa57deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 14 Oct 2021 12:34:11 +0200 Subject: [PATCH 345/424] update installation instruction --- Makefile.PL | 3 +++ docs/Installation.md | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index f5e02acd6..843e13f86 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -9,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, @@ -35,6 +37,7 @@ requires 'Zonemaster::Engine' => 4.002, 'Zonemaster::LDNS' => 2.002, 'Plack::Middleware::ReverseProxy' => 0, + 'Locale::TextDomain' => 1.20, ; test_requires 'DBD::SQLite'; diff --git a/docs/Installation.md b/docs/Installation.md index 5adf2a92c..1d4ebe409 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -65,7 +65,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 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 perl-libintl redhat-lsb-core ``` > **Note:** perl-Net-Server and perl-Test-Warn are listed here even though they @@ -75,7 +75,7 @@ sudo yum -y install jq perl-Class-Method-Modifiers perl-Config-IniFiles perl-DBD Install dependencies not available from binary packages: ```sh -sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman Plack::Middleware::ReverseProxy +sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman Plack::Middleware::ReverseProxy Locale::Msgfmt ``` Install Zonemaster::Backend: @@ -204,7 +204,7 @@ 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 perl-doc starman +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 libintl-perl liblocale-msgfmt-perl perl-doc starman ``` > **Note**: libio-stringy-perl is listed here even though it's not a direct @@ -330,7 +330,7 @@ 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-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 p5-Locale-Msgfmt ``` From a9232e48775123405da8c86155cb1a03b05bfd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 14 Oct 2021 14:15:50 +0200 Subject: [PATCH 346/424] update doc --- docs/Telemetry.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/Telemetry.md b/docs/Telemetry.md index b2a23406c..daa276ce8 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -20,7 +20,7 @@ If [enabled][metrics feature], [Statsd][statsd] compatible metrics are available Testing the metrics feature can be as easy as running a listening UDP server like ```sh -ns -lu 8125 +ns -lup 8125 ``` This should be enough to see the metrics emitted by Zonemaster. @@ -28,6 +28,33 @@ 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 + ``` +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#d1-metrics [statsd]: https://github.com/statsd/statsd [StatsD exporter]: https://github.com/grafana/statsd_exporter From ea2237e2a80079806c08c3053a744fce739c2832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 14 Oct 2021 14:31:55 +0200 Subject: [PATCH 347/424] add new metrics --- docs/Telemetry.md | 28 +++++++++++++++++++--------- script/zonemaster_backend_testagent | 9 +++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/Telemetry.md b/docs/Telemetry.md index daa276ce8..632fdb935 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -4,15 +4,17 @@ If [enabled][metrics feature], [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.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. | +| 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 @@ -45,6 +47,14 @@ StatsD metrics can be integrated to a number of metrics backend like Prometheus 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 ``` diff --git a/script/zonemaster_backend_testagent b/script/zonemaster_backend_testagent index cc44b329a..aeae013da 100755 --- a/script/zonemaster_backend_testagent +++ b/script/zonemaster_backend_testagent @@ -135,18 +135,27 @@ sub main { my $agent = Zonemaster::Backend::TestAgent->new( { config => $self->config } ); 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 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, $self->config->ZONEMASTER_maximal_number_of_retries, ); + + 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 From 0a123ec0baaaeba5ba28a9de803847bdd711af0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 14 Oct 2021 14:42:11 +0200 Subject: [PATCH 348/424] update config comments --- lib/Zonemaster/Backend/Config.pm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 8cd2721d2..7bcee566b 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -582,6 +582,24 @@ Returns a number. =cut +=head2 METRICS_statsd_host + +Get the value of +L. + +Returns a string. + +=cut + +=head2 METRICS_statsd_port + +Get the value of +L. + +Returns a number. + +=cut + =head2 RPCAPI_enable_add_api_user Get the value of From 64a10b6d051e3fe6dfda4af2be065ebcf883b894 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 14 Oct 2021 14:56:02 +0200 Subject: [PATCH 349/424] Fixes paste errors --- docs/Installation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index b7acd8a7f..bd61b21f4 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -600,8 +600,8 @@ prompted. 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 `/etc/zonemaster/backend_config.ini` file with database name, username -and password if non-default values are used. +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. @@ -726,8 +726,8 @@ psql -U postgres -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" psql -U postgres -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` -Update the `/etc/zonemaster/backend_config.ini` file with database name, username -and password if non-default values are used. +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. From 8e91dc9ed4d063cb30b3d10cf9a39fea5b771a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 20 Oct 2021 14:19:29 +0200 Subject: [PATCH 350/424] update doc --- docs/Telemetry.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/Telemetry.md b/docs/Telemetry.md index 632fdb935..e533dbbb2 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -2,7 +2,8 @@ ## Metrics -If [enabled][metrics feature], [Statsd][statsd] compatible metrics are available to use: +If enabled in the [Metrics section][metrics feature] in the configuration file, +[Statsd][statsd] compatible metrics are available to use: | Name | Type | Description | | ------------------------------------------------ | ------- | ----------- | @@ -67,6 +68,6 @@ StatsD metrics can be integrated to a number of metrics backend like Prometheus [metrics feature]: Installation.md#d1-metrics [statsd]: https://github.com/statsd/statsd -[StatsD exporter]: https://github.com/grafana/statsd_exporter +[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 From d85e7228058201a3f80eea73289ff73abdea0b33 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 12 Oct 2021 17:35:31 +0200 Subject: [PATCH 351/424] Use single quotes --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 8f9eca960..e88cbe15d 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -108,7 +108,7 @@ sub create_db { } if ( not exists($indexes->{test_results__domain_undelegated}) ) { $dbh->do( - "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" + 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' ); } From 0b42189eb0a2a69733a0524d40a3432ed1db3a57 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 12 Oct 2021 17:35:42 +0200 Subject: [PATCH 352/424] New index to speed up query to retrieve next test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looking for the next test to run involves searching among the tests which haven't started (progress = 0) and sorting them by descending priority. On huge "test_results" table (millions of entries) this query can take several seconds. Using an index here can drastically speed up the query to milleseconds (measurements made with PostgreSQL). This new index is only added for PostgreSQL. For SQLite, due to performance issue, it is not recommended to use it for installation with heavy load (such as batch function). When it comes to MariaDB and MySQL it seems that descending indexes were not supported (and silently ignored [1]). MySQL 8 now supports descending indexes [2]. However this is not yet the case for MariaDB (there is a workaround [3]). For this reason the index is not created for MariaDB/MySQL. If performance is an issue, it is possible to create the index (when using MySQL >= 8) or to use a workaround (involving a new PERSISTENT column with reverse ordered values of priority, so that an ascending index can be created on it). [1]: https://jira.mariadb.org/browse/MDEV-13756 [2]: https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html [3]: https://falseisnotnull.wordpress.com/2014/04/25/descending-indexes-in-mariadb/ --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index e88cbe15d..35d352a84 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -111,6 +111,13 @@ sub create_db { 'CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)' ); } + if ( not exists($indexes->{test_results__progress_priority_id}) ) { + # this index helps speed up query time to retrieve the next test to + # perform when using batches + $dbh->do( + 'CREATE INDEX test_results__progress_priority_id ON test_results (progress, priority DESC, id) WHERE (progress = 0)' + ); + } #################################################################### From ce2bd6c50f0823ddd7a0c13eb56a0a6267920099 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 18:11:52 +0200 Subject: [PATCH 353/424] Add new index to patch script (PostgreSQL) --- share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl index 7b1fc6324..b2cd6b3b8 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +++ b/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl @@ -57,6 +57,13 @@ sub patch_db { }; print( "Error while updating the index: " . $@ ) if ($@); + # New index + eval { + # clause IF NOT EXISTS available since PostgreSQL >= 9.5 + $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 From c9057fabbc6d573b1614ddec7cba94c99a3d91be Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Mon, 27 Sep 2021 17:33:11 +0200 Subject: [PATCH 354/424] Remove database upgrade from installation process --- docs/Installation.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 04e2812c6..f6443a0f8 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -126,12 +126,6 @@ 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 -"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, -then go to section "[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 SQLite (CentOS) @@ -263,13 +257,6 @@ 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 -"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, -then go to section "[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 SQLite (Debian/Ubuntu) @@ -380,13 +367,6 @@ 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 -"[Upgrade Zonemaster database]" first. If you instead want to start from afresh, -then go to section "[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) @@ -806,7 +786,6 @@ cpanm Net::Statsd ------- [Backend configuration]: Configuration.md -[Cleaning up the database]: #10-cleaning-up-the-database [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 From c538657c110221537480f9fd7ce2da6519cd5dfd Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 28 Sep 2021 14:15:01 +0200 Subject: [PATCH 355/424] New Upgrade document --- README.md | 27 ++------------------------ docs/Installation.md | 6 ++---- docs/Upgrade.md | 45 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 docs/Upgrade.md diff --git a/README.md b/README.md index c7d3e908f..74f64abf6 100644 --- a/README.md +++ b/README.md @@ -27,27 +27,8 @@ instructions](https://github.com/zonemaster/zonemaster-engine/blob/master/docs/I ### Upgrade -If you upgrade Zonemaster-Backend and want to keep the content of the database -(SQLite, MySQL/MariaDB or PostgreSQL) 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. - -Created by Zonemaster-Backend 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 -At least 5.0.2 but older than 7.0.0 | [Upgrade to 7.0.0] | - -If the database was created before Zonemaster-Backend version 5.0.2, then you -have to upgrade it 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 @@ -84,8 +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 to 7.0.0]: docs/upgrade_db_zonemaster_backend_ver_7.0.0.md +[Upgrade document]: docs/Upgrade.md diff --git a/docs/Installation.md b/docs/Installation.md index f6443a0f8..856290238 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -454,7 +454,7 @@ Once the number reaches 100% a JSON object is printed and zmtest terminates. 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. +[Upgrade document]. ## 8. Installation with MariaDB @@ -797,9 +797,7 @@ cpanm Net::Statsd [PostgreSQL instructions CentOS]: #91-postgresql-centos [PostgreSQL instructions Debian]: #92-postgresql-debianubuntu [PostgreSQL instructions FreeBSD]: #93-postgresql-freebsd -[README.md-upgrade]: README.md#upgrade -[Upgrade Zonemaster database]: #7-upgrade-zonemaster-database -[Upgrade database]: #7-upgrade-zonemaster-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 diff --git a/docs/Upgrade.md b/docs/Upgrade.md new file mode 100644 index 000000000..c0ec51a77 --- /dev/null +++ b/docs/Upgrade.md @@ -0,0 +1,45 @@ +# 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. + + +## 2. Upgrading Zonemaster::Backend + +To upgrade Zonemaster::Backend perform the following tasks: + + 1. stop the `zm-rpcapi` and `zm-testagent` daemons + 2. install the latest version from `cpanm` with `cpanm Zonemaster::Backend` + 3. apply any instructions specific to this new release + 4. start the `zm-rpcapi` and `zm-testagend` daemons + + +### 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 < 7.0.0 | [Upgrade to 7.0.0] | + + +[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 to 7.0.0]: docs/upgrade_db_zonemaster_backend_ver_7.0.0.md From fd3af4f6efe29ac8ec9ef0d625bf7c8d9a24e870 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 28 Sep 2021 14:18:00 +0200 Subject: [PATCH 356/424] Move upgrade instructions to subfolder * new docs/upgrade folder * adapt MANIFEST --- MANIFEST | 10 +++++----- docs/Upgrade.md | 10 +++++----- .../upgrade_zonemaster_backend_ver_1.0.3.md} | 0 .../upgrade_zonemaster_backend_ver_1.1.0.md} | 0 .../upgrade_zonemaster_backend_ver_5.0.0.md} | 0 .../upgrade_zonemaster_backend_ver_5.0.2.md} | 0 .../upgrade_zonemaster_backend_ver_7.0.0.md} | 0 7 files changed, 10 insertions(+), 10 deletions(-) rename docs/{upgrade_db_zonemaster_backend_ver_1.0.3.md => upgrade/upgrade_zonemaster_backend_ver_1.0.3.md} (100%) rename docs/{upgrade_db_zonemaster_backend_ver_1.1.0.md => upgrade/upgrade_zonemaster_backend_ver_1.1.0.md} (100%) rename docs/{upgrade_db_zonemaster_backend_ver_5.0.0.md => upgrade/upgrade_zonemaster_backend_ver_5.0.0.md} (100%) rename docs/{upgrade_db_zonemaster_backend_ver_5.0.2.md => upgrade/upgrade_zonemaster_backend_ver_5.0.2.md} (100%) rename docs/{upgrade_db_zonemaster_backend_ver_7.0.0.md => upgrade/upgrade_zonemaster_backend_ver_7.0.0.md} (100%) diff --git a/MANIFEST b/MANIFEST index c1a1dfbf7..61d07a3a0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -8,11 +8,11 @@ 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_db_zonemaster_backend_ver_7.0.0.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_7.0.0.md inc/Module/Install.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm diff --git a/docs/Upgrade.md b/docs/Upgrade.md index c0ec51a77..a753fd070 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -38,8 +38,8 @@ Current Zonemaster::Backend version | Link to instructions | Comments 5.0.2 ≤ version < 7.0.0 | [Upgrade to 7.0.0] | -[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 to 7.0.0]: docs/upgrade_db_zonemaster_backend_ver_7.0.0.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 7.0.0]: upgrade/upgrade_zonemaster_backend_ver_7.0.0.md 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 100% rename from docs/upgrade_db_zonemaster_backend_ver_1.0.3.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md diff --git a/docs/upgrade_db_zonemaster_backend_ver_1.1.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md similarity index 100% rename from docs/upgrade_db_zonemaster_backend_ver_1.1.0.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md 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 100% rename from docs/upgrade_db_zonemaster_backend_ver_5.0.0.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md 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 100% rename from docs/upgrade_db_zonemaster_backend_ver_5.0.2.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md diff --git a/docs/upgrade_db_zonemaster_backend_ver_7.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md similarity index 100% rename from docs/upgrade_db_zonemaster_backend_ver_7.0.0.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md From 72ac7885fe672bf8f4dd394388b97f9835504095 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 28 Sep 2021 18:43:34 +0200 Subject: [PATCH 357/424] Editorial updates --- .../upgrade_zonemaster_backend_ver_1.0.3.md | 8 ++++++-- .../upgrade_zonemaster_backend_ver_1.1.0.md | 16 ++++++++++------ .../upgrade_zonemaster_backend_ver_5.0.0.md | 8 ++++++-- .../upgrade_zonemaster_backend_ver_5.0.2.md | 8 ++++++-- .../upgrade_zonemaster_backend_ver_7.0.0.md | 8 ++++++-- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md index 2ba2a7764..4ff38b8c2 100644 --- a/docs/upgrade/upgrade_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 diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md index a8d1a2567..e256eb23d 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_1.1.0.md @@ -1,15 +1,19 @@ -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. +# Upgrade to 1.1.0 -MySQL +## 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 +### PostgreSQL -``` +```sql ALTER TABLE test_results ADD queue INTEGER DEFAULT 0; ``` diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md index 76c0c6e32..5b21266e7 100644 --- a/docs/upgrade/upgrade_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 diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md index e389d0596..910c77fea 100644 --- a/docs/upgrade/upgrade_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 diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md index fd4b9d592..8dacfd89a 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md @@ -1,5 +1,9 @@ -If your zonemaster database was created by a Zonemaster-Backend version smaller -than v7.0.0, and not upgraded, use the instructions in this file. +# Upgrade to 7.0.0 + +## Upgrading the database + +If your Zonemaster database was created by a Zonemaster-Backend version smaller +than v7.0.0, and not upgraded, use the following instructions. ### SQLite From 3eb07bfa2826677003b89acea79e9268699758a5 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 29 Sep 2021 12:17:55 +0200 Subject: [PATCH 358/424] Remove unecessary imports Addresses #618 --- share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl | 3 --- share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl | 3 --- share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl | 3 --- share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl | 3 --- share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl | 3 --- 5 files changed, 15 deletions(-) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl index 1d1510d5f..dfdcf1912 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/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); diff --git a/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl b/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl index 842e27b5a..975b1afe5 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/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); diff --git a/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl b/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl index a44f1265c..aead54574 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl +++ b/share/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); diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl index 1871017b2..093966a09 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/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); diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl b/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl index cb42c2f76..206d29f97 100644 --- a/share/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl +++ b/share/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); From d77e5e6de21f0f9360c42dca8bed40b168f8a4b1 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 29 Sep 2021 12:25:54 +0200 Subject: [PATCH 359/424] Use spaces instead of tabs --- ...h_mysql_db_zonemaster_backend_ver_1.0.3.pl | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl b/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl index dfdcf1912..1316d2a71 100644 --- a/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl +++ b/share/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl @@ -22,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; + ' ); } From 12793eab050a4532a83de1870c385671bae54a4a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 29 Sep 2021 12:32:46 +0200 Subject: [PATCH 360/424] Move patches to subfolder * new folder share/patch * adapat MANIFEST --- MANIFEST | 18 +++++++++--------- .../upgrade_zonemaster_backend_ver_1.0.3.md | 4 ++-- .../upgrade_zonemaster_backend_ver_5.0.0.md | 4 ++-- .../upgrade_zonemaster_backend_ver_5.0.2.md | 2 +- .../upgrade_zonemaster_backend_ver_7.0.0.md | 4 ++-- share/patch/README.txt | 2 ++ ...ch_mysql_db_zonemaster_backend_ver_1.0.3.pl | 0 ...ch_mysql_db_zonemaster_backend_ver_5.0.0.pl | 0 ...ch_mysql_db_zonemaster_backend_ver_5.0.2.pl | 0 ...ch_mysql_db_zonemaster_backend_ver_7.0.0.pl | 0 ...stgresql_db_zonemaster_backend_ver_1.0.3.pl | 0 ...stgresql_db_zonemaster_backend_ver_5.0.0.pl | 0 ...stgresql_db_zonemaster_backend_ver_7.0.0.pl | 0 ...h_sqlite_db_zonemaster_backend_ver_7.0.0.pl | 0 share/patch_db_README.txt | 2 -- 15 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 share/patch/README.txt rename share/{ => patch}/patch_mysql_db_zonemaster_backend_ver_1.0.3.pl (100%) rename share/{ => patch}/patch_mysql_db_zonemaster_backend_ver_5.0.0.pl (100%) rename share/{ => patch}/patch_mysql_db_zonemaster_backend_ver_5.0.2.pl (100%) rename share/{ => patch}/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl (100%) rename share/{ => patch}/patch_postgresql_db_zonemaster_backend_ver_1.0.3.pl (100%) rename share/{ => patch}/patch_postgresql_db_zonemaster_backend_ver_5.0.0.pl (100%) rename share/{ => patch}/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl (100%) rename share/{ => patch}/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl (100%) delete mode 100644 share/patch_db_README.txt diff --git a/MANIFEST b/MANIFEST index 61d07a3a0..a67969c38 100644 --- a/MANIFEST +++ b/MANIFEST @@ -51,15 +51,15 @@ share/cleanup-mysql.sql share/cleanup-postgres.sql share/create_db.pl share/freebsd-pwd.conf -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_mysql_db_zonemaster_backend_ver_7.0.0.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_postgresql_db_zonemaster_backend_ver_7.0.0.pl -share/patch_sqlite_db_zonemaster_backend_ver_7.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_7.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_7.0.0.pl +share/patch/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl share/tmpfiles.conf share/travis_mysql_backend_config.ini share/travis_postgresql_backend_config.ini diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md b/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md index 4ff38b8c2..a72af2eb0 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_1.0.3.md @@ -19,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 @@ -27,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_5.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md index 5b21266e7..68a235d19 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.0.md @@ -19,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 ``` @@ -28,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/upgrade_zonemaster_backend_ver_5.0.2.md b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md index 910c77fea..660e63d83 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_5.0.2.md @@ -19,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_7.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md index 8dacfd89a..1f311aae6 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md @@ -19,7 +19,7 @@ perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +perl patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl ``` @@ -28,5 +28,5 @@ perl patch_mysql_db_zonemaster_backend_ver_7.0.0.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +perl patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl ``` 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 100% 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 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 100% 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 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 100% 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 diff --git a/share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl similarity index 100% rename from share/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl 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 100% 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 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 100% 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 diff --git a/share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl similarity index 100% rename from share/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl diff --git a/share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl similarity index 100% rename from share/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl 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 From 458dc829d3fa863a1e76ff98460593a3d008dd4e Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 29 Sep 2021 14:21:25 +0200 Subject: [PATCH 361/424] Fix next release version number v2021.1.3 was a a public fix release which bumped the version of the Backend to 7.0.0. Next release will be at least 8.0.0 since breaking changes in the database will occur. --- MANIFEST | 8 ++++---- docs/Upgrade.md | 4 ++-- ....0.0.md => upgrade_zonemaster_backend_ver_8.0.0.md} | 10 +++++----- ... => patch_mysql_db_zonemaster_backend_ver_8.0.0.pl} | 0 ...atch_postgresql_db_zonemaster_backend_ver_8.0.0.pl} | 0 ...=> patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl} | 0 6 files changed, 11 insertions(+), 11 deletions(-) rename docs/upgrade/{upgrade_zonemaster_backend_ver_7.0.0.md => upgrade_zonemaster_backend_ver_8.0.0.md} (64%) rename share/patch/{patch_mysql_db_zonemaster_backend_ver_7.0.0.pl => patch_mysql_db_zonemaster_backend_ver_8.0.0.pl} (100%) rename share/patch/{patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl => patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl} (100%) rename share/patch/{patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl => patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl} (100%) diff --git a/MANIFEST b/MANIFEST index a67969c38..96b7bf80f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -12,7 +12,7 @@ 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_7.0.0.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 @@ -55,11 +55,11 @@ 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_7.0.0.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_7.0.0.pl -share/patch/patch_sqlite_db_zonemaster_backend_ver_7.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 diff --git a/docs/Upgrade.md b/docs/Upgrade.md index a753fd070..35c3a412b 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -35,11 +35,11 @@ Current Zonemaster::Backend version | Link to instructions | Comments 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 < 7.0.0 | [Upgrade to 7.0.0] | + 5.0.2 ≤ version < 8.0.0 | [Upgrade to 8.0.0] | [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 7.0.0]: upgrade/upgrade_zonemaster_backend_ver_7.0.0.md +[Upgrade to 8.0.0]: upgrade/upgrade_zonemaster_backend_ver_8.0.0.md diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md similarity index 64% rename from docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md rename to docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 1f311aae6..8bd97fecb 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_7.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -1,16 +1,16 @@ -# Upgrade to 7.0.0 +# Upgrade to 8.0.0 ## Upgrading the database If your Zonemaster database was created by a Zonemaster-Backend version smaller -than v7.0.0, and not upgraded, use the following instructions. +than v8.0.0, and not upgraded, use the following instructions. ### SQLite Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl +perl patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl ``` @@ -19,7 +19,7 @@ perl patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl +perl patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl ``` @@ -28,5 +28,5 @@ perl patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl +perl patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl ``` diff --git a/share/patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl similarity index 100% rename from share/patch/patch_mysql_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl diff --git a/share/patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl similarity index 100% rename from share/patch/patch_postgresql_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl diff --git a/share/patch/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl b/share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl similarity index 100% rename from share/patch/patch_sqlite_db_zonemaster_backend_ver_7.0.0.pl rename to share/patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl From 73e2816f39820b51b2c4d2a9bf8c353a464e1afc Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 6 Oct 2021 17:19:28 +0200 Subject: [PATCH 362/424] Fix typo --- docs/Upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index 35c3a412b..edb731b3b 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -14,7 +14,7 @@ To upgrade Zonemaster::Backend perform the following tasks: 1. stop the `zm-rpcapi` and `zm-testagent` daemons 2. install the latest version from `cpanm` with `cpanm Zonemaster::Backend` 3. apply any instructions specific to this new release - 4. start the `zm-rpcapi` and `zm-testagend` daemons + 4. start the `zm-rpcapi` and `zm-testagent` daemons ### Specific upgrade instructions From bf3e54e4b5967cab90d4477c8c55988418cf97cf Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 6 Oct 2021 17:20:10 +0200 Subject: [PATCH 363/424] Pointer documentating upgrade of other components Points to the Zonemaster::Engine installation document since there is currently no Upgrade document for Zonemaster::Engine. --- docs/Upgrade.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index edb731b3b..020ad302a 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -7,7 +7,12 @@ Zonemaster::Backend component. An upgrade usually consist of an upgrade script to upgrade the database, and instructions to install new dependencies. -## 2. Upgrading Zonemaster::Backend +## 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: @@ -43,3 +48,4 @@ Current Zonemaster::Backend version | Link to instructions | Comments [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 From 19864647d7241021b0709decafd394bbfac9adea Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 6 Oct 2021 17:32:22 +0200 Subject: [PATCH 364/424] Add new dependencies to the upgrade document --- .../upgrade_zonemaster_backend_ver_8.0.0.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 8bd97fecb..495ef339a 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -1,5 +1,28 @@ # Upgrade to 8.0.0 +## New dependencies + +Depending on the used OS, run the corresponding command. + +### FreeBSD + +```sh +pkg install p5-Plack-Middleware-ReverseProxy +``` + +### Debian / Ubuntu + +```sh +sudo apt-get install libplack-middleware-reverseproxy-perl +``` + +### Centos + +```sh +sudo cpanm Plack::Middleware::ReverseProxy +``` + + ## Upgrading the database If your Zonemaster database was created by a Zonemaster-Backend version smaller From a21531f113eea26ba018000936f1b8c0cc3a2512 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 13:42:38 +0200 Subject: [PATCH 365/424] Add a note when upgrading from an older version --- docs/Upgrade.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index 020ad302a..1b9465343 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -6,6 +6,11 @@ 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 @@ -43,6 +48,7 @@ Current Zonemaster::Backend version | Link to instructions | Comments 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 From 63062e8bf062cd8ca9302944cb2903b4415100ad Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 13:58:50 +0200 Subject: [PATCH 366/424] Rename section to be more generic --- docs/Installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 856290238..01744f9f7 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -25,7 +25,7 @@ * [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. Upgrade Zonemaster database](#7-upgrade-zonemaster-database) +* [7. Upgrade of Zonemaster Backend](#7-upgrade-of-zonemaster-backend) * [8. Installation with MariaDB](#8-installation-with-mariadb) * [8.1 MariaDB (CentOS)](#81-mariadb-centos) * [8.2. MariaDB (Debian/Ubuntu)](#82-mariadb-debianubuntu) @@ -449,7 +449,7 @@ Once the number reaches 100% a JSON object is printed and zmtest terminates. * For a JSON-RPC API, see the Zonemaster::Backend [JSON-RPC API] documentation. -## 7. Upgrade Zonemaster database +## 7. Upgrade of Zonemaster Backend If you upgrade your Zonemaster installation with a newer version of Zonemaster-Backend and keep the database, then you might have to upgrade the From 70b4878537756df595fa3b5ba1c604373ad17728 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 16:48:51 +0200 Subject: [PATCH 367/424] Specific daemons names on FreeBSD --- docs/Upgrade.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index 1b9465343..aec7fd84d 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -21,10 +21,12 @@ within the [Zonemaster::Engine installation] document. To upgrade Zonemaster::Backend perform the following tasks: - 1. stop the `zm-rpcapi` and `zm-testagent` daemons + 1. stop the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + `zm_testagent` on FreeBSD) 2. install the latest version from `cpanm` with `cpanm Zonemaster::Backend` 3. apply any instructions specific to this new release - 4. start the `zm-rpcapi` and `zm-testagent` daemons + 4. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + `zm_testagent` on FreeBSD) ### Specific upgrade instructions From 46423a095acea18f2c7fcbe40321a6f69cefa2c8 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 16:49:39 +0200 Subject: [PATCH 368/424] Editorial update --- docs/Upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index aec7fd84d..dbffe1e72 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -23,7 +23,7 @@ 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. install the latest version from `cpanm` with `cpanm Zonemaster::Backend` + 2. install the latest version from CPAN with `cpanm Zonemaster::Backend` 3. apply any instructions specific to this new release 4. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and `zm_testagent` on FreeBSD) From 74966b1d4f461e6d7f4d7e76f385328809d89e8f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 16:59:01 +0200 Subject: [PATCH 369/424] Move the upgrade note to the top of Installation --- docs/Installation.md | 91 +++++++++++++++++++++----------------------- docs/Telemetry.md | 2 +- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 01744f9f7..9b4d0689e 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -25,27 +25,31 @@ * [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. Upgrade of Zonemaster Backend](#7-upgrade-of-zonemaster-backend) -* [8. Installation with MariaDB](#8-installation-with-mariadb) - * [8.1 MariaDB (CentOS)](#81-mariadb-centos) - * [8.2. MariaDB (Debian/Ubuntu)](#82-mariadb-debianubuntu) - * [8.3. MySQL (FreeBSD)](#83-mysql-freebsd) -* [9. Installation with PostgreSQL](#9-installation-with-postgresql) - * [9.1. PostgreSQL (CentOS)](#91-postgresql-centos) - * [9.2. PostgreSQL (Debian/Ubuntu)](#92-postgresql-debianubuntu) - * [9.3. PostgreSQL (FreeBSD)](#93-postgresql-freebsd) -* [10. Cleaning up the database](#10-cleaning-up-the-database) - * [10.1. MariaDB and MySQL](#101-mariadb-and-mysql) - * [10.2. PostgreSQL](#102-postgresql) - * [10.3. SQLite](#103-sqlite) -* [11. Optional features](#11-optional-features) - * [11.1. Metrics](#111-metrics) +* [7. Installation with MariaDB](#7-installation-with-mariadb) + * [7.1 MariaDB (CentOS)](#71-mariadb-centos) + * [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 (CentOS)](#81-postgresql-centos) + * [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 @@ -449,20 +453,12 @@ Once the number reaches 100% a JSON object is printed and zmtest terminates. * For a JSON-RPC API, see the Zonemaster::Backend [JSON-RPC API] documentation. -## 7. Upgrade of Zonemaster Backend - -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 document]. - - -## 8. Installation with MariaDB +## 7. Installation with MariaDB First follow the installation instructions for the OS in question, and then go to this section to install MariaDB. -### 8.1. MariaDB (CentOS) +### 7.1. MariaDB (CentOS) Configure Zonemaster::Backend to use the correct database engine: @@ -501,7 +497,7 @@ Now go back to "[Database configuration](#33-database-configuration-centos)" to create the database tables and then continue with the steps after that. -### 8.2. MariaDB (Debian/Ubuntu) +### 7.2. MariaDB (Debian/Ubuntu) Configure Zonemaster::Backend to use the correct database engine: @@ -538,7 +534,7 @@ Now go back to "[Database configuration](#43-database-configuration-debianubuntu to create the database tables and then continue with the steps after that. -### 8.3. MySQL (FreeBSD) +### 7.3. MySQL (FreeBSD) > MariaDB is not compatible with Zonemaster on FreeBSD. MySQL is used instead. @@ -588,12 +584,12 @@ Now go back to "[Database configuration](#53-database-configuration-freebsd)" to create the database tables and then continue with the steps after that. -## 9. Installation with PostgreSQL +## 8. Installation with PostgreSQL First follow the installation instructions for the OS in question, and then go to this section to install PostgreSQL. -### 9.1. PostgreSQL (CentOS) +### 8.1. PostgreSQL (CentOS) Configure Zonemaster::Backend to use the correct database engine: @@ -644,7 +640,7 @@ Now go back to "[Database configuration](#33-database-configuration-centos)" to create the database tables and then continue with the steps after that. -### 9.2. PostgreSQL (Debian/Ubuntu) +### 8.2. PostgreSQL (Debian/Ubuntu) Configure Zonemaster::Backend to use the correct database engine: @@ -680,7 +676,7 @@ Now go back to "[Database configuration](#43-database-configuration-debianubuntu to create the database tables and then continue with the steps after that. -### 9.3. PostgreSQL (FreeBSD) +### 8.3. PostgreSQL (FreeBSD) Configure Zonemaster::Backend to use the correct database engine: @@ -714,7 +710,7 @@ Now go back to "[Database configuration](#53-database-configuration-freebsd)" to create the database tables and then continue with the steps after that. -## 10. 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` @@ -722,7 +718,7 @@ 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). -### 10.1. MariaDB and MySQL +### 9.1. MariaDB and MySQL CentOS, Debian and Ubuntu: @@ -736,7 +732,7 @@ FreeBSD (you will get prompted for MySQL password): mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql ``` -### 10.2. PostgreSQL +### 9.2. PostgreSQL CentOS, Debian and Ubuntu: @@ -750,13 +746,13 @@ FreeBSD (as root): psql -U postgres -f `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-postgres.sql ``` -### 10.3. SQLite +### 9.3. SQLite Remove the database file and recreate it following the installation instructions above. -## 11. Optional features +## 10. Optional features -### 11.1 Metrics +### 10.1 Metrics Statsd metrics are available, to enable the feature install the additional `Net::Statsd` module. See the [configuration][Backend configuration] to @@ -764,20 +760,20 @@ configure the receiver. The list of metrics is available in the [Telemetry document][metrics]; -### 11.1.1 Installation on Centos +### 10.1.1 Installation on Centos ```sh sudo cpanm Net::Statsd ``` -### 11.1.2 Installation on Debian / Ubuntu +### 10.1.2 Installation on Debian / Ubuntu ```sh sudo apt install libnet-statsd-perl ``` -### 11.1.3 Installation on Freebsd +### 10.1.3 Installation on Freebsd ```sh cpanm Net::Statsd @@ -789,14 +785,15 @@ cpanm Net::Statsd [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 CentOS]: #81-mariadb-centos -[MariaDB instructions Debian]: #82-mariadb-debianubuntu -[MariaDB instructions FreeBSD]: #83-mysql-freebsd -[metrics]: Telemetry.md#metrics +[MariaDB instructions CentOS]: #71-mariadb-centos +[MariaDB instructions Debian]: #72-mariadb-debianubuntu +[MariaDB instructions FreeBSD]: #73-mysql-freebsd +[metrics]: Telemetry.md#metrics [Post-installation]: #6-post-installation -[PostgreSQL instructions CentOS]: #91-postgresql-centos -[PostgreSQL instructions Debian]: #92-postgresql-debianubuntu -[PostgreSQL instructions FreeBSD]: #93-postgresql-freebsd +[PostgreSQL instructions CentOS]: #81-postgresql-centos +[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 diff --git a/docs/Telemetry.md b/docs/Telemetry.md index e533dbbb2..fdc2c435f 100644 --- a/docs/Telemetry.md +++ b/docs/Telemetry.md @@ -66,7 +66,7 @@ StatsD metrics can be integrated to a number of metrics backend like Prometheus curl localhost:9102/metrics | grep zonemaster ``` -[metrics feature]: Installation.md#d1-metrics +[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 From 7914d9c0a4ac8ff1eaa363a28bdcd9513ff26e3f Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 17:44:37 +0200 Subject: [PATCH 370/424] Split upgrade document instructions in two If the upgrade contains new dependencies, they should be installed before upgrading Zonemaster::Backend. However, the upgrade may need to install new files, they require the new version of Zonemaster::Backend to be installed in order to retrieve the correct files. --- docs/Upgrade.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index dbffe1e72..c00ad729b 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -23,9 +23,10 @@ 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. install the latest version from CPAN with `cpanm Zonemaster::Backend` - 3. apply any instructions specific to this new release - 4. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + 2. install any new dependencies (see corresponding upgrade document) + 3. install the latest version from CPAN with `cpanm Zonemaster::Backend` + 4. apply any remaining instructions specific to this new release + 5. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and `zm_testagent` on FreeBSD) From 734830cbab4814daa33bfe0aea0c471c67701336 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 13 Oct 2021 17:56:12 +0200 Subject: [PATCH 371/424] Upgrade instruction for updated init scripts --- .../upgrade_zonemaster_backend_ver_8.0.0.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 495ef339a..99bfbc70e 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -23,6 +23,29 @@ sudo cpanm Plack::Middleware::ReverseProxy ``` +## Upgrading init scripts + +Go to the share folder: +``` +cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` +``` + +And then install the `zm-rpcapi` daemon (`zm_rpcapi` on FreeBSD). + +### FreeBSD + +``` +su -l +install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi +``` + +### Debian / Ubuntu / CentOS + +``` +sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi +``` + + ## Upgrading the database If your Zonemaster database was created by a Zonemaster-Backend version smaller From 26a99da6e578f5294fbfe4ccdd67a86480490df8 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:03:10 +0200 Subject: [PATCH 372/424] Gain root privileges --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 99bfbc70e..61154c0cd 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -1,5 +1,11 @@ # Upgrade to 8.0.0 +On FreeBSD run the following command first to become root: + +```sh +su -l +``` + ## New dependencies Depending on the used OS, run the corresponding command. @@ -35,7 +41,6 @@ And then install the `zm-rpcapi` daemon (`zm_rpcapi` on FreeBSD). ### FreeBSD ``` -su -l install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi ``` @@ -51,6 +56,8 @@ sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi If your Zonemaster database was created by a Zonemaster-Backend version smaller than v8.0.0, and not upgraded, use the following instructions. +> You may need to run the command with `sudo`. + ### SQLite Run From c062da3bd944dc94fc052613bd6c4507c3d3a588 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:05:21 +0200 Subject: [PATCH 373/424] Add a note about upgrading big databases --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 61154c0cd..85a52e59b 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -56,6 +56,9 @@ sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi 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 From a2b3aeaa368fc1eb7798c18106e7b2c6ca466dbc Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:05:49 +0200 Subject: [PATCH 374/424] Reorder sections to be aligned with other documents --- .../upgrade_zonemaster_backend_ver_8.0.0.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 85a52e59b..5be84e343 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -10,10 +10,10 @@ su -l Depending on the used OS, run the corresponding command. -### FreeBSD +### Centos ```sh -pkg install p5-Plack-Middleware-ReverseProxy +sudo cpanm Plack::Middleware::ReverseProxy ``` ### Debian / Ubuntu @@ -22,10 +22,10 @@ pkg install p5-Plack-Middleware-ReverseProxy sudo apt-get install libplack-middleware-reverseproxy-perl ``` -### Centos +### FreeBSD ```sh -sudo cpanm Plack::Middleware::ReverseProxy +pkg install p5-Plack-Middleware-ReverseProxy ``` @@ -38,18 +38,24 @@ cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` And then install the `zm-rpcapi` daemon (`zm_rpcapi` on FreeBSD). -### FreeBSD +### CentOS ``` -install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi +sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi ``` -### Debian / Ubuntu / CentOS +### Debian / Ubuntu ``` sudo install -v -m 755 ./zm-rpcapi.lsb /etc/init.d/zm-rpcapi ``` +### FreeBSD + +``` +install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi +``` + ## Upgrading the database @@ -69,7 +75,6 @@ cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backe perl patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl ``` - ### MySQL (or MariaDB) Run @@ -78,7 +83,6 @@ cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backe perl patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl ``` - ### PostgreSQL Run From 0e2d2c931d1ea6c923dff0f912c210dc5c2a8143 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:12:22 +0200 Subject: [PATCH 375/424] Upgrade note about changes in the ini file --- .../upgrade_zonemaster_backend_ver_8.0.0.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 5be84e343..b15d22d1d 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -29,6 +29,20 @@ pkg install p5-Plack-Middleware-ReverseProxy ``` +## 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 Go to the share folder: @@ -90,3 +104,5 @@ Run 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 From ceb297bccba04b2e56a44f3e5062fd8b5ac13aff Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:14:09 +0200 Subject: [PATCH 376/424] Editorial updates on section about init script --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index b15d22d1d..d6fe41ae5 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -45,28 +45,27 @@ enable_add_api_user = yes ## Upgrading init scripts -Go to the share folder: -``` -cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` -``` - -And then install the `zm-rpcapi` daemon (`zm_rpcapi` on FreeBSD). +The `zm-rpcapi` (`zm_rpcapi` on FreeBSD) init script has been updated. It needs +to be reinstalled. ### CentOS ``` +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 ``` From acbd050e1223f7469a17ac65726db40b2dee2e45 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Tue, 19 Oct 2021 12:15:03 +0200 Subject: [PATCH 377/424] Editorial update on dependencies --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index d6fe41ae5..14d3074c4 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -8,7 +8,8 @@ su -l ## New dependencies -Depending on the used OS, run the corresponding command. +Zonemaster::Backend requires new dependencies. Depending on the used OS, run +the corresponding command. ### Centos From d06e9f216e1d34c7d50728ab960241be23f2f3ee Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 21 Oct 2021 11:51:33 +0200 Subject: [PATCH 378/424] Editorial update --- docs/Installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Installation.md b/docs/Installation.md index 9b4d0689e..e53fed715 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -758,7 +758,7 @@ 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]; +The list of metrics is available in the [Telemetry document][metrics]. ### 10.1.1 Installation on Centos From 28b1a74b6325aff8fc268489e2e450d7abefe295 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 20 Oct 2021 16:57:07 +0200 Subject: [PATCH 379/424] Drop support for PostgreSQL < 10 --- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 57 +++++++------------ ...tgresql_db_zonemaster_backend_ver_8.0.0.pl | 3 - 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 35d352a84..85b6b4cea 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -81,43 +81,26 @@ sub create_db { ' ) or die Zonemaster::Backend::Error::Internal->new( reason => "PostgreSQL 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 PostgreSQL < 9.5 - - # retrieve all indexes by key name - my $indexes = $dbh->selectall_hashref( "SELECT indexname FROM pg_indexes WHERE tablename = 'test_results'", 'indexname' ); - 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)' - ); - } - if ( not exists($indexes->{test_results__progress_priority_id}) ) { - # this index helps speed up query time to retrieve the next test to - # perform when using batches - $dbh->do( - 'CREATE INDEX test_results__progress_priority_id ON test_results (progress, priority DESC, id) WHERE (progress = 0)' - ); - } + $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)' + ); #################################################################### 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 index b2cd6b3b8..c9946e68d 100644 --- 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 @@ -28,7 +28,6 @@ sub patch_db { # Update index eval { - # clause IF EXISTS available since PostgreSQL >= 9.2 $dbh->do( "DROP INDEX IF EXISTS test_results__params_deterministic_hash" ); $dbh->do( "CREATE INDEX test_results__fingerprint ON test_results (fingerprint)" ); }; @@ -51,7 +50,6 @@ sub patch_db { # Update index eval { - # clause IF EXISTS available since PostgreSQL >= 9.2 $dbh->do( "DROP INDEX IF EXISTS test_results__domain_undelegated" ); $dbh->do( "CREATE INDEX test_results__domain_undelegated ON test_results (domain, undelegated)" ); }; @@ -59,7 +57,6 @@ sub patch_db { # New index eval { - # clause IF NOT EXISTS available since PostgreSQL >= 9.5 $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 ($@); From dd375d8abc50a5ed07552ecc56fdb4fc7aa55a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 27 Oct 2021 12:03:33 +0200 Subject: [PATCH 380/424] improve install doc --- Makefile.PL | 2 +- docs/Installation.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index 843e13f86..612c2f7f8 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -57,8 +57,8 @@ 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'; }; diff --git a/docs/Installation.md b/docs/Installation.md index 1d4ebe409..5618f2af1 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -330,7 +330,7 @@ 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 p5-Locale-libintl p5-Locale-Msgfmt +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 p5-Locale-libintl p5-Locale-Msgfmt gmake ``` From 4b212a4a36cdee611677e2931ffad7b7050fb983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 27 Oct 2021 12:04:03 +0200 Subject: [PATCH 381/424] remove default language config --- docs/API.md | 6 ++---- docs/Configuration.md | 5 ----- lib/Zonemaster/Backend/Config.pm | 11 ----------- lib/Zonemaster/Backend/RPCAPI.pm | 16 +++++++++------- t/test01.t | 6 ------ 5 files changed, 11 insertions(+), 33 deletions(-) diff --git a/docs/API.md b/docs/API.md index b8678a0a2..79b62accc 100644 --- a/docs/API.md +++ b/docs/API.md @@ -687,8 +687,7 @@ 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, the default value is defined in the [language section] - of the configuration. + translation, if not provided messages will be untranslated. #### `"result"` @@ -786,8 +785,7 @@ An object with the following properties: * `"priority"`: A *priority*, optional. (default: `10`) * `"queue"`: A *queue*, optional. (default: `0`) * `"language"`: A [Language Tag], optional, used for validation error messages - translation, the default value is defined in the [language section] - of the configuration. + translation, if not provided messages will be untranslated. > TODO: Clarify the purpose of each `"params"` property. > diff --git a/docs/Configuration.md b/docs/Configuration.md index e589d825b..a1e75cc2c 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -271,11 +271,6 @@ English is the Zonemaster default language, but it can be blocked from being allowed by RPC-API by including some `locale tag` in the configuration, but none starting with language code for English ("en"). -The first language in the list will be used as the default for the RPC API -error messages. If translation not available, then the error messages will be -send untranslated, i.e. in English. See the [API documentation] to know which -methods support error message localization. - #### Out-of-the-box support The default installation and configuration supports the diff --git a/lib/Zonemaster/Backend/Config.pm b/lib/Zonemaster/Backend/Config.pm index 0d910001d..870332103 100644 --- a/lib/Zonemaster/Backend/Config.pm +++ b/lib/Zonemaster/Backend/Config.pm @@ -286,13 +286,8 @@ sub parse { if ( defined( my $value = $get_and_clear->( 'LANGUAGE', 'locale' ) ) ) { if ( $value ne "" ) { $obj->_reset_LANGUAGE_locale(); - my $default_set = 0; for my $locale_tag ( split / +/, $value ) { $obj->_add_LANGUAGE_locale( $locale_tag ); - if ( not $default_set ) { - $obj->_set_LANGUAGE_default_locale( $locale_tag ); - $default_set = 1; - } } } } @@ -512,10 +507,6 @@ E.g.: }, ) -=head2 LANGUAGE_default_locale -Get the first locale passed to the L in the configuration file. -Returns a string. - =head2 PUBLIC_PROFILES @@ -616,7 +607,6 @@ sub POSTGRESQL_password { return $_[0]->{_POSTGR sub POSTGRESQL_database { return $_[0]->{_POSTGRESQL_database}; } sub SQLITE_database_file { return $_[0]->{_SQLITE_database_file}; } sub LANGUAGE_locale { return %{ $_[0]->{_LANGUAGE_locale} }; } -sub LANGUAGE_default_locale { return $_[0]->{_LANGUAGE_default_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}; } @@ -631,7 +621,6 @@ sub RPCAPI_enable_add_batch_job { return $_[0]->{_RPCAPI # Compile time generation of setters for the properties documented above UNITCHECK { _create_setter( '_set_DB_polling_interval', '_DB_polling_interval', \&untaint_strictly_positive_millis ); - _create_setter( '_set_LANGUAGE_default_locale', '_LANGUAGE_default_locale', \&untaint_locale_tag ); _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 ); diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 4f17aa743..ee024779a 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -17,7 +17,8 @@ use Mojo::JSON::Pointer; use Scalar::Util qw(blessed); use JSON::Validator::Schema::Draft7; use Locale::TextDomain qw[Zonemaster-Backend]; -use Locale::Messages qw[setlocale LC_MESSAGES]; +use Locale::Messages qw[LC_MESSAGES LC_ALL]; +use POSIX qw (setlocale); use Encode; # Zonemaster Modules @@ -677,8 +678,6 @@ sub _get_locale { if (defined $locale) { $locale .= '.UTF-8'; - } else { - $locale = $self->{config}->LANGUAGE_default_locale } return $locale, \@error, @@ -691,10 +690,13 @@ sub _set_error_message_locale { my ($locale, $locale_error) = $self->_get_locale( $params ); push @error_response, @{$locale_error}; - $locale //= $self->{config}->LANGUAGE_default_locale; + if (not defined $locale or $locale eq "") { + # Don't translate message if locale is not defined + $locale = "C"; + } - $ENV{LANGUAGE} = $locale; - setlocale( LC_MESSAGES, $locale ); + # Use POSIX implementation instead of Locale::Messages wrapper + setlocale( LC_ALL, $locale ); return @error_response; } @@ -727,7 +729,7 @@ sub jsonrpc_validate { id => $jsonrpc_request->{id}, error => { code => '-32602', - message => decode_utf8(__ "Missing 'params' object"), + message => "Missing 'params' object", } }; } diff --git a/t/test01.t b/t/test01.t index 80e12c3b9..375ded070 100644 --- a/t/test01.t +++ b/t/test01.t @@ -132,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); From 9d38748b7715f5d0d295adccf6dbfaae1ea202d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 27 Oct 2021 12:07:29 +0200 Subject: [PATCH 382/424] change languages order --- share/backend_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index d250e9a2e..b1bea6e79 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -40,7 +40,7 @@ database_file = /var/lib/zonemaster/db.sqlite [LANGUAGE] # The first language in this list will be used as default for error message translation -locale = en_US da_DK fi_FI fr_FR nb_NO sv_SE +locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE [PUBLIC PROFILES] #example_profile_1=/example/directory/test1_profile.json From 277771cbe832079b8bca0e813d67e20efe20d904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 27 Oct 2021 14:34:16 +0200 Subject: [PATCH 383/424] remove comment --- share/backend_config.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/share/backend_config.ini b/share/backend_config.ini index b1bea6e79..66dbe8444 100755 --- a/share/backend_config.ini +++ b/share/backend_config.ini @@ -39,7 +39,6 @@ database_file = /var/lib/zonemaster/db.sqlite #enable_add_batch_job = no [LANGUAGE] -# The first language in this list will be used as default for error message translation locale = da_DK en_US fi_FI fr_FR nb_NO sv_SE [PUBLIC PROFILES] From 4959503cdc54a09826a6f16955fcab7a0db46f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 28 Oct 2021 10:59:30 +0200 Subject: [PATCH 384/424] remove unused link --- docs/API.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 79b62accc..13f11aa28 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1481,7 +1481,6 @@ The `"params"` object sent to `start_domain_test` or `add_batch_job` when the *t [JSON-RPC 2.0]: https://www.jsonrpc.org/specification [LANGUAGE.locale]: Configuration.md#locale [Language tag]: #language-tag -[language section]: Configuration.md#usage [Name server]: #name-server [Privilege levels]: #privilege-levels [Profile name]: #profile-name From 423f0b7d433fdf18f085348282be303a80b8ac24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 28 Oct 2021 11:00:11 +0200 Subject: [PATCH 385/424] rework makefile --- Makefile.PL | 4 ++-- share/GNUmakefile | 13 ++++--------- share/update-po | 9 +++++++++ 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100755 share/update-po diff --git a/Makefile.PL b/Makefile.PL index 612c2f7f8..21d286375 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -69,10 +69,10 @@ sub MY::postamble { # Make FreeBSD use gmake for share/Makefile $text = 'GMAKE ?= "gmake"' . "\n" . 'pure_all :: share/Makefile' . "\n" - . "\t" . 'cd share && $(GMAKE) touch-po all' . "\n"; + . "\t" . 'cd share && $(GMAKE) all' . "\n"; } else { $text = 'pure_all :: share/Makefile' . "\n" - . "\t" . 'cd share && $(MAKE) touch-po all' . "\n"; + . "\t" . 'cd share && $(MAKE) all' . "\n"; }; return $text; }; diff --git a/share/GNUmakefile b/share/GNUmakefile index 64d362930..dd50d13d3 100644 --- a/share/GNUmakefile +++ b/share/GNUmakefile @@ -1,8 +1,8 @@ .POSIX: .SUFFIXES: .po .mo -.PHONY: all check-msg-args dist extract-pot tidy-po show-fuzzy touch-po update-po +.PHONY: all check-msg-args dist extract-pot tidy-po show-fuzzy update-po -POFILES := $(shell find . -maxdepth 1 -type f -name '*.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) @@ -19,19 +19,14 @@ tidy-po: trap 'rm -rf "$$tmpdir"' EXIT ;\ for f in $(POFILES) ; do msgcat $$f -o $$tmpdir/$$f && mv -f $$tmpdir/$$f $$f ; done -touch-po: - @touch $(POTFILE) $(POFILES) - -update-po: extract-pot $(POFILES) +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 -$(POFILES): $(POTFILE) - @msgmerge --update --backup=none --quiet --no-location $(MSGMERGE_OPTS) $@ $(POTFILE) - .po.mo: @msgfmt -o $@ $< @mkdir -p locale/`basename $@ .mo`/LC_MESSAGES 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" From 655efb9d257264d4f1efaa0e01a3974a0923d379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 28 Oct 2021 11:22:18 +0200 Subject: [PATCH 386/424] update language param syntax test --- t/test_validate_syntax.t | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index 314f2aeef..a5e4743ed 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -257,15 +257,23 @@ subtest 'Everything but NoWarnings' => sub { 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; - $frontend_params->{language} = "zz"; - cmp_ok( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), '>', 0, encode_utf8( 'Invalid language, "zz" unknown' ) ) - or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); - $frontend_params->{language} = "fr-FR"; - cmp_ok( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), '>', 0, encode_utf8( 'Invalid language, should be underscore not hyphen' ) ) - or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + { + $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 ); + } - $frontend_params->{language} = "nb_NO"; - is( scalar $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ), 0, encode_utf8( 'Valid language' ) ) - or diag( encode_json $engine->validate_params( $start_domain_test_schema, $start_domain_test_validate_syntax, $frontend_params ) ); + { + $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 ); + } + + { + $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 ); + } }; From 4f1d66c6519f0569e2532c67f2b837f502266718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 4 Nov 2021 10:03:09 +0100 Subject: [PATCH 387/424] add update-po to manifest.skip --- MANIFEST.SKIP | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 70f616c01..c249ee5c7 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -11,6 +11,7 @@ \.po$ ^share/[^/]*\.mo$ ^share/Zonemaster-Backend.pot$ +^share/update-po$ #!start included /usr/share/perl/5.20/ExtUtils/MANIFEST.SKIP # Avoid version control files. From bae515530ebdb3dd5196deece1cd55a3e06dbb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 4 Nov 2021 10:03:36 +0100 Subject: [PATCH 388/424] update frontend params locally --- t/test_validate_syntax.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/test_validate_syntax.t b/t/test_validate_syntax.t index a5e4743ed..d5e257202 100644 --- a/t/test_validate_syntax.t +++ b/t/test_validate_syntax.t @@ -259,20 +259,20 @@ subtest 'Everything but NoWarnings' => sub { $frontend_params->{ds_info}->[0]->{keytag} = 5000; { - $frontend_params->{language} = "zz"; + 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 ); } { - $frontend_params->{language} = "fr-FR"; + 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 ); } { - $frontend_params->{language} = "nb_NO"; + 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 ); } From 4eb9b0b9168e157358ec5332508d0c0d69236ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 4 Nov 2021 12:00:18 +0100 Subject: [PATCH 389/424] unset language env --- script/zonemaster_backend_rpcapi.psgi | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script/zonemaster_backend_rpcapi.psgi b/script/zonemaster_backend_rpcapi.psgi index 7a6dbd91e..d0293ea49 100644 --- a/script/zonemaster_backend_rpcapi.psgi +++ b/script/zonemaster_backend_rpcapi.psgi @@ -19,7 +19,10 @@ 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; From 2504fa4a46d36d7a4b51ceddddec648b3e1369de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 4 Nov 2021 14:20:06 +0100 Subject: [PATCH 390/424] update installation doc --- docs/Installation.md | 73 ++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index e53fed715..e515840fd 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -4,12 +4,12 @@ * [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 (CentOS)](#32-database-engine-installation-centos) - * [3.3 Database configuration (CentOS)](#33-database-configuration-centos) - * [3.4 Service configuration and startup (CentOS)](#34-service-configuration-and-startup-centos) - * [3.5 Post-installation (CentOS)](#35-post-installation-centos) +* [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) @@ -69,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. @@ -79,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-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 ``` > **Note:** perl-Net-Server and perl-Test-Warn are listed here even though they @@ -124,7 +124,7 @@ sudo install -v -m 755 ./tmpfiles.conf /usr/lib/tmpfiles.d/zonemaster.conf > previous version of Zonemaster-Backend). -### 3.2 Database engine installation (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. @@ -132,7 +132,7 @@ of operating system version and database engine version is supported. The installation instructions below assumes that this is a new installation. -#### 3.2.1 Instructions for SQLite (CentOS) +#### 3.2.1 Instructions for SQLite (Rocky Linux) > **Note:** Zonemaster with SQLite is not meant for an installation with heavy > load. @@ -147,13 +147,13 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster > for details. -#### 3.2.2 Instructions for other engines (CentOS) +#### 3.2.2 Instructions for other engines (Rocky Linux) See sections for [MariaDB][MariaDB instructions CentOS] and [PostgreSQL][PostgreSQL instructions CentOS]. -### 3.3 Database configuration (CentOS) +### 3.3 Database configuration (Rocky Linux) Create the database tables: @@ -162,7 +162,7 @@ sudo -u zonemaster $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir(" ``` -### 3.4 Service configuration and startup (CentOS) +### 3.4 Service configuration and startup (Rocky Linux) Make sure our tmpfiles configuration takes effect: @@ -180,7 +180,7 @@ sudo systemctl start zm-testagent ``` -### 3.5 Post-installation (CentOS) +### 3.5 Post-installation (Rocky Linux) See the [post-installation] section for post-installation matters. @@ -212,18 +212,12 @@ 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 perl-doc 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 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 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 -``` - Install Zonemaster::Backend: ```sh @@ -458,7 +452,7 @@ Once the number reaches 100% a JSON object is printed and zmtest terminates. First follow the installation instructions for the OS in question, and then go to this section to install MariaDB. -### 7.1. MariaDB (CentOS) +### 7.1. MariaDB (Rocky Linux) Configure Zonemaster::Backend to use the correct database engine: @@ -493,7 +487,7 @@ 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-centos)" +Now go back to "[Database configuration](#33-database-configuration-rocky-linux)" to create the database tables and then continue with the steps after that. @@ -589,7 +583,7 @@ to create the database tables and then continue with the steps after that. First follow the installation instructions for the OS in question, and then go to this section to install PostgreSQL. -### 8.1. PostgreSQL (CentOS) +### 8.1. PostgreSQL (Rocky Linux) Configure Zonemaster::Backend to use the correct database engine: @@ -601,26 +595,13 @@ sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.in 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 - ``` - -* 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 - ``` +```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 +``` 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 @@ -636,7 +617,7 @@ sudo -u postgres psql -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENC 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-centos)" +Now go back to "[Database configuration](#33-database-configuration-rocky-linux)" to create the database tables and then continue with the steps after that. From d75bb4e80b8098bc6ea9d81f5e90b0e259d3fec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 8 Nov 2021 18:26:48 +0100 Subject: [PATCH 391/424] update upgrade document --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 14d3074c4..12a6139de 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -13,6 +13,10 @@ the corresponding command. ### Centos +```sh +sudo dnf install perl-libintl +``` + ```sh sudo cpanm Plack::Middleware::ReverseProxy ``` @@ -20,13 +24,13 @@ sudo cpanm Plack::Middleware::ReverseProxy ### Debian / Ubuntu ```sh -sudo apt-get install libplack-middleware-reverseproxy-perl +sudo apt-get install libplack-middleware-reverseproxy-perl libintl-perl ``` ### FreeBSD ```sh -pkg install p5-Plack-Middleware-ReverseProxy +pkg install p5-Plack-Middleware-ReverseProxy p5-Locale-libintl ``` From 317d05557cac9c0e5119d299ba3ded9f16fcd3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 8 Nov 2021 18:47:14 +0100 Subject: [PATCH 392/424] update upgrade document --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 14d3074c4..f029fd919 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -11,7 +11,7 @@ su -l Zonemaster::Backend requires new dependencies. Depending on the used OS, run the corresponding command. -### Centos +### Rocky Linux ```sh sudo cpanm Plack::Middleware::ReverseProxy From 7be1f965553651355c5876131c691c5ce55d88d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 8 Nov 2021 18:51:31 +0100 Subject: [PATCH 393/424] update remaining centos mention --- docs/Configuration.md | 2 +- docs/Installation.md | 18 +++++++++--------- .../upgrade_zonemaster_backend_ver_8.0.0.md | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 19a3c5b4a..866b6c9db 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -43,7 +43,7 @@ ## Introduction Zonemaster *Backend* is configured in -`/etc/zonemaster/backend_config.ini` (CentOS, Debian and Ubuntu) or +`/etc/zonemaster/backend_config.ini` (Rocky Linux, Debian and Ubuntu) or `/usr/local/etc/zonemaster/backend_config.ini` (FreeBSD). Following [Installation instructions] will create the file with factory settings. diff --git a/docs/Installation.md b/docs/Installation.md index e515840fd..8c710d923 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -26,11 +26,11 @@ * [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 (CentOS)](#71-mariadb-centos) + * [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 (CentOS)](#81-postgresql-centos) + * [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) @@ -149,8 +149,8 @@ sudo install -v -m 755 -o zonemaster -g zonemaster -d /var/lib/zonemaster #### 3.2.2 Instructions for other engines (Rocky Linux) -See sections for [MariaDB][MariaDB instructions CentOS] and -[PostgreSQL][PostgreSQL instructions CentOS]. +See sections for [MariaDB][MariaDB instructions Rocky Linux] and +[PostgreSQL][PostgreSQL instructions Rocky Linux]. ### 3.3 Database configuration (Rocky Linux) @@ -701,7 +701,7 @@ database (obviously taking all data with it). ### 9.1. MariaDB and MySQL -CentOS, Debian and Ubuntu: +Rocky Linux, Debian and Ubuntu: ```sh sudo mysql --user=root < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'`/cleanup-mysql.sql @@ -715,7 +715,7 @@ mysql --user=root -p < `perl -MFile::ShareDir -le 'print File::ShareDir::dist_di ### 9.2. PostgreSQL -CentOS, Debian and Ubuntu: +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 @@ -741,7 +741,7 @@ configure the receiver. The list of metrics is available in the [Telemetry document][metrics]. -### 10.1.1 Installation on Centos +### 10.1.1 Installation on Rocky Linux ```sh sudo cpanm Net::Statsd @@ -766,12 +766,12 @@ cpanm Net::Statsd [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 CentOS]: #71-mariadb-centos +[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 CentOS]: #81-postgresql-centos +[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 diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index f029fd919..7157f3840 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -49,7 +49,7 @@ enable_add_api_user = yes The `zm-rpcapi` (`zm_rpcapi` on FreeBSD) init script has been updated. It needs to be reinstalled. -### CentOS +### Rocky Linux ``` cd `perl -MFile::ShareDir=dist_dir -E 'say dist_dir("Zonemaster-Backend")'` From 5e58e125ee5fbecd39dd2d38dc51a8d28090b537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 10 Nov 2021 10:09:47 +0100 Subject: [PATCH 394/424] remove the retry column in db --- lib/Zonemaster/Backend/DB/MySQL.pm | 3 +-- lib/Zonemaster/Backend/DB/PostgreSQL.pm | 3 +-- lib/Zonemaster/Backend/DB/SQLite.pm | 3 +-- ...h_mysql_db_zonemaster_backend_ver_8.0.0.pl | 6 ++++++ ...tgresql_db_zonemaster_backend_ver_8.0.0.pl | 8 ++++++- ..._sqlite_db_zonemaster_backend_ver_8.0.0.pl | 21 ++++++++++++++++++- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/Zonemaster/Backend/DB/MySQL.pm b/lib/Zonemaster/Backend/DB/MySQL.pm index ddd9e7679..170aaa510 100644 --- a/lib/Zonemaster/Backend/DB/MySQL.pm +++ b/lib/Zonemaster/Backend/DB/MySQL.pm @@ -79,8 +79,7 @@ sub create_db { fingerprint character varying(32), params blob NOT NULL, results mediumblob DEFAULT NULL, - undelegated integer NOT NULL DEFAULT 0, - nb_retries integer NOT NULL DEFAULT 0 + 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() ); diff --git a/lib/Zonemaster/Backend/DB/PostgreSQL.pm b/lib/Zonemaster/Backend/DB/PostgreSQL.pm index 8db80f07c..f987c76eb 100644 --- a/lib/Zonemaster/Backend/DB/PostgreSQL.pm +++ b/lib/Zonemaster/Backend/DB/PostgreSQL.pm @@ -75,8 +75,7 @@ sub create_db { fingerprint varchar(32), params json NOT NULL, undelegated integer NOT NULL DEFAULT 0, - results json, - nb_retries 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() ); diff --git a/lib/Zonemaster/Backend/DB/SQLite.pm b/lib/Zonemaster/Backend/DB/SQLite.pm index 89db4e17e..0f508f747 100644 --- a/lib/Zonemaster/Backend/DB/SQLite.pm +++ b/lib/Zonemaster/Backend/DB/SQLite.pm @@ -70,8 +70,7 @@ sub create_db { fingerprint character varying(32), params text NOT NULL, results text DEFAULT NULL, - undelegated boolean NOT NULL DEFAULT false, - nb_retries integer NOT NULL DEFAULT 0 + 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() ); 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 index 6e5fa81fc..9ca8fb648 100644 --- 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 @@ -65,6 +65,12 @@ sub patch_db { $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/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl b/share/patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl index c9946e68d..08b1ba03c 100644 --- 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 @@ -94,10 +94,16 @@ sub patch_db { print( "Error while changing DB schema: " . $@ ) if ($@); # update the columns - $dbh->do( "UPDATE users SET username = (user_info->>'username'), api_key = (user_info->>'api_key')" ); + 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 index fb4b675bb..b1adbd471 100644 --- 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 @@ -31,7 +31,26 @@ sub patch_db { $db->create_db(); # populate it - $dbh->do('INSERT INTO test_results SELECT * FROM test_results_old'); + # - 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'); From 71e95b8bf7edf52738a0605aadd0d09c8e1d1e76 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Thu, 11 Nov 2021 17:27:05 +0100 Subject: [PATCH 395/424] Adds missing file to MANIFEST --- MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST b/MANIFEST index 96b7bf80f..fb671a2eb 100644 --- a/MANIFEST +++ b/MANIFEST @@ -8,6 +8,7 @@ docs/GettingStarted.md docs/Installation.md docs/Telemetry.md docs/TypographicConventions.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 From 3bca699fefca896f51b1ea8ae63a6733fd066638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Fri, 12 Nov 2021 10:46:03 +0100 Subject: [PATCH 396/424] update configuration doc --- docs/Configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 866b6c9db..e8a333b57 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -43,7 +43,7 @@ ## Introduction Zonemaster *Backend* is configured in -`/etc/zonemaster/backend_config.ini` (Rocky Linux, 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. From f3f4d77a328eee6244dec5a2634071038318d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Fri, 12 Nov 2021 10:46:30 +0100 Subject: [PATCH 397/424] remove note about old init scripts --- docs/Installation.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 8c710d923..fa1b813b0 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -119,11 +119,6 @@ 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 (Rocky Linux) Check the [declaration of prerequisites] to make sure your preferred combination From 6fdae4a4195a82d5daf6f7abde8f9cb756208197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Fri, 12 Nov 2021 11:04:05 +0100 Subject: [PATCH 398/424] update api doc --- docs/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 32871aa9f..84fe43879 100644 --- a/docs/API.md +++ b/docs/API.md @@ -687,7 +687,7 @@ 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. + translation, if not provided messages will be untranslated (in English). #### `"result"` From 82e24a01ae066fab88f39d83d711c77b66ce7166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 15 Nov 2021 12:34:41 +0100 Subject: [PATCH 399/424] remove locale::msgfmt from dependencies --- docs/Installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 4d2ea8d04..f2ee42b1e 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -89,7 +89,7 @@ sudo yum -y install jq perl-Class-Method-Modifiers perl-Config-IniFiles perl-DBD Install dependencies not available from binary packages: ```sh -sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman Plack::Middleware::ReverseProxy Locale::Msgfmt +sudo cpanm Daemon::Control JSON::Validator Log::Any Log::Any::Adapter::Dispatch Starman Plack::Middleware::ReverseProxy ``` Install Zonemaster::Backend: @@ -212,7 +212,7 @@ 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 libintl-perl liblocale-msgfmt-perl perl-doc starman +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 libintl-perl perl-doc starman ``` > **Note**: libio-stringy-perl is listed here even though it's not a direct @@ -332,7 +332,7 @@ 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 p5-Locale-libintl p5-Locale-Msgfmt gmake +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 p5-Locale-libintl gmake ``` From 2d76595af3dcf72e6ff4634540eb106464d41b73 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 16 Nov 2021 17:32:02 +0100 Subject: [PATCH 400/424] Create PO files * Update header of fr.po to conform to zonemaster model * Create empty PO files for languages that are missing --- share/da.po | 13 +++++++++++++ share/es.po | 13 +++++++++++++ share/fi.po | 13 +++++++++++++ share/fr.po | 16 +++++----------- share/nb.po | 13 +++++++++++++ 5 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 share/da.po create mode 100644 share/es.po create mode 100644 share/fi.po create mode 100644 share/nb.po 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..a258d2e71 --- /dev/null +++ b/share/fi.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: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + diff --git a/share/fr.po b/share/fr.po index b8a04715f..cea0e4f8f 100644 --- a/share/fr.po +++ b/share/fr.po @@ -1,18 +1,12 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"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: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \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" 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" + From 9a69eda874ac59bcf6bcaaa313423278add9a2bd Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 16 Nov 2021 17:50:39 +0100 Subject: [PATCH 401/424] Adds PO file for translation to Swedish --- share/sv.po | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 share/sv.po diff --git a/share/sv.po b/share/sv.po new file mode 100644 index 000000000..dc64aff99 --- /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-16 17:49+0100\n" +"PO-Revision-Date: 2021-11-16 17:49+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-" +"labelformat" + +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ännamnets teckeninnehåll 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 en 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 ska vara ett positivt heltal" + +msgid "Digest type must be a positive integer" +msgstr "Digest-typ ska var ett positivt heltal" + +msgid "Keytag must be a positive integer" +msgstr "Keytag ska vara ett positivt heltal" + +msgid "Invalid language tag format" +msgstr "Ogiltigt format på språkkoden" From ceab0db269a29acfd4b76ddfa58988ed763d7913 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 17 Nov 2021 08:58:33 +0100 Subject: [PATCH 402/424] Adds minor revision --- share/sv.po | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/sv.po b/share/sv.po index dc64aff99..cb8e3ddbb 100644 --- a/share/sv.po +++ b/share/sv.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-16 17:49+0100\n" -"PO-Revision-Date: 2021-11-16 17:49+0100\n" +"POT-Creation-Date: 2021-11-17 08:57+0100\n" +"PO-Revision-Date: 2021-11-17 08:57+0100\n" "Last-Translator: mats.dufberg@iis.se\n" "Language-Team: Zonemaster project\n" "Language: sv\n" @@ -19,7 +19,7 @@ msgid "" "label" msgstr "" "Domännamnet är inte ett giltigt IDN-namn och kan inte konverteras till A-" -"labelformat" +"label-format" msgid "The domain name contains non-ascii characters and IDNA is not installed" msgstr "" @@ -27,7 +27,7 @@ msgstr "" "installerat" msgid "The domain name character(s) are not supported" -msgstr "Domännamnets teckeninnehåll stöds inte" +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" @@ -58,7 +58,7 @@ msgstr "" "icke-ASCII-strängar inte kan hanteras korrekt" msgid "The domain name contains a character or characters not supported" -msgstr "Domännamnet innehåller en eller flera tecken som inte stöds" +msgstr "Domännamnet innehåller ett eller flera tecken som inte stöds" msgid "Invalid digest format" msgstr "Ogiltigt format på DS-digest" From 5bb18a606925755927ea9a86f80bf87834406479 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 14:27:30 +0100 Subject: [PATCH 403/424] Bump JSON::Validator version We use the Draft7 schema class that was introduced in 4.00. --- Makefile.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.PL b/Makefile.PL index 21d286375..91b4ca039 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -21,7 +21,7 @@ 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, From 555dedcc0dac2c190b0c05d557891e4fc55d3252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 17 Nov 2021 14:47:50 +0100 Subject: [PATCH 404/424] remove unsupported perl version from travis --- .travis.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab8c776c0..59e158a49 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" @@ -17,13 +17,6 @@ jobs: - perl: "5.28" 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: From a414abd07f5aab1645b2d51ca755304d0d380a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 17 Nov 2021 15:24:02 +0100 Subject: [PATCH 405/424] revert missing line --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 59e158a49..0abd65fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ jobs: - perl: "5.28" 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 addons: apt: From af1b5d27f98d313ccccf7ed78102c16301c04e9a Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 16:18:06 +0100 Subject: [PATCH 406/424] Instruction specific to Ubuntu The version of the "libjson-validator-perl" package is too old and don't include Draft7 schema class. Install JSON::Validator from CPAN. --- docs/Installation.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/Installation.md b/docs/Installation.md index 78e0a694c..4651eb38e 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -209,10 +209,14 @@ Install dependencies available from binary packages: ```sh 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 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. +* On Ubuntu, install the following dependency from CPAN: + ``` + sudo cpanm JSON::Validator + ``` + Install Zonemaster::Backend: ```sh From 8af4975f48aaa53fe994d3768341eb7bede95b0d Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 15:22:01 +0100 Subject: [PATCH 407/424] Explicitely depends on Mojolicious * Needed to use Mojo::JSON::Pointer Using the same version as in JSON::Validator * Add Mojolicious dependency to installation instruction --- Makefile.PL | 1 + docs/Installation.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index 91b4ca039..b52e4ecb3 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -26,6 +26,7 @@ requires 'Log::Any::Adapter::Dispatch' => 0, 'Log::Dispatch' => 0, 'LWP::UserAgent' => 0, + 'Mojolicious' => 7.28, 'Moose' => 2.04, 'Parallel::ForkManager' => 1.12, 'Plack::Builder' => 0, diff --git a/docs/Installation.md b/docs/Installation.md index 4651eb38e..91f8f6ab7 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -79,7 +79,7 @@ for Zonemaster::Backend, see the [declaration of prerequisites]. Install dependencies available from binary packages: ```sh -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-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 +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 @@ -207,7 +207,7 @@ sv_SE.utf8 Install dependencies available from binary packages: ```sh -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 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 +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. @@ -325,7 +325,7 @@ 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 p5-Locale-libintl gmake +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 ``` From 5262b023f962b5fe258878b966aed3fce32ba899 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Wed, 17 Nov 2021 17:36:10 +0100 Subject: [PATCH 408/424] Editorial updates --- share/sv.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/sv.po b/share/sv.po index cb8e3ddbb..9d77b1696 100644 --- a/share/sv.po +++ b/share/sv.po @@ -3,7 +3,7 @@ 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 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" @@ -64,13 +64,13 @@ msgid "Invalid digest format" msgstr "Ogiltigt format på DS-digest" msgid "Algorithm must be a positive integer" -msgstr "Algoritm ska vara ett positivt heltal" +msgstr "Algoritm måste vara ett positivt heltal" msgid "Digest type must be a positive integer" -msgstr "Digest-typ ska var ett positivt heltal" +msgstr "Digest-typ måste vara ett positivt heltal" msgid "Keytag must be a positive integer" -msgstr "Keytag ska vara ett positivt heltal" +msgstr "Keytag måste vara ett positivt heltal" msgid "Invalid language tag format" msgstr "Ogiltigt format på språkkoden" From 726ff52227127fb669b6179a224a9bb49e8076ca Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 17:44:58 +0100 Subject: [PATCH 409/424] Fix DROP USER for MySQL --- share/cleanup-mysql.sql | 1 - 1 file changed, 1 deletion(-) 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'@'%'; From 7950d47b487e86a08842a6f814fdcf3488245882 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 18:02:12 +0100 Subject: [PATCH 410/424] Add note on default values in cleanup scripts --- docs/Installation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Installation.md b/docs/Installation.md index 78e0a694c..9562ba956 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -694,6 +694,8 @@ 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). +> Each script uses default values, you may need to adapt them to your setup. + ### 9.1. MariaDB and MySQL Rocky Linux, Debian and Ubuntu: From 4db7bed25f5216ba00d05b49785281d1ad45c7fc Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 18:06:44 +0100 Subject: [PATCH 411/424] Upgrade instruction for the new dependency --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index a94eeae3f..d941ad72f 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -14,7 +14,7 @@ the corresponding command. ### Rocky Linux ```sh -sudo dnf install perl-libintl +sudo dnf install perl-libintl perl-Mojolicious ``` ```sh @@ -24,13 +24,13 @@ sudo cpanm Plack::Middleware::ReverseProxy ### Debian / Ubuntu ```sh -sudo apt-get install libplack-middleware-reverseproxy-perl libintl-perl +sudo apt-get install libplack-middleware-reverseproxy-perl libintl-perl libmojolicious-perl ``` ### FreeBSD ```sh -pkg install p5-Plack-Middleware-ReverseProxy p5-Locale-libintl +pkg install p5-Plack-Middleware-ReverseProxy p5-Locale-libintl p5-Mojolicious ``` From 7473c3182d81925570c2e2d6d4aeee28e0042e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 17 Nov 2021 18:24:22 +0100 Subject: [PATCH 412/424] clean old files --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index a94eeae3f..ca4691e0c 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -75,6 +75,14 @@ install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi ``` +## Cleaning old files + +Run +```sh +cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +sudo rm patch_*.pl +``` + ## Upgrading the database If your Zonemaster database was created by a Zonemaster-Backend version smaller @@ -90,7 +98,7 @@ than v8.0.0, and not upgraded, use the following instructions. Run ```sh cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -perl patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl +perl patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl ``` ### MySQL (or MariaDB) From ffdf288c0921ec5f09f87072a91e233b7c8f5921 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Wed, 17 Nov 2021 18:28:09 +0100 Subject: [PATCH 413/424] Missing upgrade dependency for Ubuntu Explicetely install JSON::Validator from CPAN for Ubuntu when upgrading. --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index d941ad72f..8aa84dc52 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -27,6 +27,12 @@ sudo cpanm Plack::Middleware::ReverseProxy sudo apt-get install libplack-middleware-reverseproxy-perl libintl-perl libmojolicious-perl ``` +#### Specific to Ubuntu + +```sh +sudo cpanm JSON::Validator +``` + ### FreeBSD ```sh From cf33e0a7070cbaf243c687261dc1803030dc424b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Wed, 17 Nov 2021 18:47:11 +0100 Subject: [PATCH 414/424] fix mysql upgrade --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index ca4691e0c..7c4d30e19 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -103,7 +103,12 @@ perl patch/patch_sqlite_db_zonemaster_backend_ver_8.0.0.pl ### MySQL (or MariaDB) -Run +First update the privileges of the `zonemaste` user: +```sh +sudo mysql -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 From b8149fe019a5ce82ce7a2cedd6b928a7c19a73cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 18 Nov 2021 10:24:21 +0100 Subject: [PATCH 415/424] fix instruction for freebsd --- .../upgrade_zonemaster_backend_ver_8.0.0.md | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 7c4d30e19..2d2d3aad1 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -77,10 +77,14 @@ install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi ## Cleaning old files -Run +### Linux +```sh +sudo rm `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')`/patch_*.pl +``` + +### FreeBSD ```sh -cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') -sudo rm patch_*.pl +rm `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')`/patch_*.pl ``` ## Upgrading the database @@ -97,20 +101,26 @@ than v8.0.0, and not upgraded, use the following instructions. Run ```sh -cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +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 `zonemaste` user: -```sh -sudo mysql -e "GRANT ALL ON zonemaster.* TO 'zonemaster'@'localhost';" -``` +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")') +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` perl patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl ``` @@ -118,7 +128,7 @@ perl patch/patch_mysql_db_zonemaster_backend_ver_8.0.0.pl Run ```sh -cd $(perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")') +cd `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")'` perl patch/patch_postgresql_db_zonemaster_backend_ver_8.0.0.pl ``` From 5b3f4f8ed523e1e4388b2b63adfa3d87ed93d98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Fri, 19 Nov 2021 07:43:03 +0100 Subject: [PATCH 416/424] Update Rocky installation instruction --- docs/Installation.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/Installation.md b/docs/Installation.md index 78e0a694c..f99eef680 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -460,7 +460,7 @@ sudo sed -i '/\bengine\b/ s/=.*/= MySQL/' /etc/zonemaster/backend_config.ini Install, configure and start database engine: ```sh -sudo yum -y install mariadb-server +sudo dnf -y install mariadb-server sudo systemctl enable mariadb sudo systemctl start mariadb ``` @@ -591,7 +591,7 @@ sudo sed -i '/\bengine\b/ s/=.*/= PostgreSQL/' /etc/zonemaster/backend_config.in Install, configure and start database engine: ```sh -sudo yum -y install postgresql-server perl-DBD-Pg +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 @@ -609,6 +609,9 @@ sudo -u postgres psql -c "CREATE USER zonemaster WITH PASSWORD 'zonemaster';" sudo -u postgres psql -c "CREATE DATABASE zonemaster WITH OWNER 'zonemaster' ENCODING 'UTF8';" ``` +> **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. From 1451991e96eb13668a27b759f7dd626c2a4059f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20P=C3=A4iv=C3=A4rinta?= Date: Sat, 20 Nov 2021 16:09:52 +0100 Subject: [PATCH 417/424] Maximize the reach of eval+handle_exception --- lib/Zonemaster/Backend/RPCAPI.pm | 71 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/lib/Zonemaster/Backend/RPCAPI.pm b/lib/Zonemaster/Backend/RPCAPI.pm index 0ef98ca14..f1be2fb97 100644 --- a/lib/Zonemaster/Backend/RPCAPI.pm +++ b/lib/Zonemaster/Backend/RPCAPI.pm @@ -134,15 +134,20 @@ $json_schemas{get_language_tags} = joi->object->strict; sub get_language_tags { my ( $self ) = @_; - my %locales = $self->{config}->LANGUAGE_locale; - my @lang_tags; - for my $lang ( sort keys %locales ) { - my @locale_tags = sort keys %{ $locales{$lang} }; - if ( scalar @locale_tags == 1 ) { - push @lang_tags, $lang; + eval { + my %locales = $self->{config}->LANGUAGE_locale; + + 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; } - push @lang_tags, @locale_tags; + }; + if ( $@ ) { + handle_exception( $@ ); } return \@lang_tags; @@ -396,11 +401,10 @@ $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 ($@) { @@ -422,24 +426,24 @@ $json_schemas{get_test_results} = { sub get_test_results { my ( $self, $params ) = @_; - # Already validated by json_validate - my ($locale, undef) = $self->_get_locale($params); - my $result; - my $translator; - $translator = Zonemaster::Backend::Translator->new; + eval{ + # Already validated by json_validate + my ( $locale, undef ) = $self->_get_locale( $params ); - my $previous_locale = $translator->locale; - if ( !$translator->locale( $locale ) ) { - handle_exception( "Failed to set locale: $locale" ); - } + my $translator; + $translator = Zonemaster::Backend::Translator->new; - eval { $translator->data } if $translator; # Provoke lazy loading of translation data + my $previous_locale = $translator->locale; + if ( !$translator->locale( $locale ) ) { + die "Failed to set locale: $locale"; + } - my $test_info; - my @zm_results; - eval{ - $test_info = $self->{db}->test_results( $params->{id} ); + eval { $translator->data } if $translator; # Provoke lazy loading of translation data + + my @zm_results; + + my $test_info = $self->{db}->test_results( $params->{id} ); foreach my $test_res ( @{ $test_info->{results} } ) { my $res; if ( $test_res->{module} eq 'NAMESERVER' ) { @@ -488,16 +492,16 @@ 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( $@ ); } - $translator->locale( $previous_locale ); - - $result = $test_info; - $result->{results} = \@zm_results; - return $result; } @@ -609,11 +613,11 @@ $json_schemas{add_batch_job} = { 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 ($@) { @@ -630,7 +634,6 @@ sub get_batch_job_result { my ( $self, $params ) = @_; my $result; - eval { my $batch_id = $params->{batch_id}; From e5750e358f494837dd2fbf04966038560a5b2001 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Tue, 23 Nov 2021 13:56:30 +0100 Subject: [PATCH 418/424] Adds missing MO files to MANIFEST --- MANIFEST | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MANIFEST b/MANIFEST index 043717885..2ab319747 100644 --- a/MANIFEST +++ b/MANIFEST @@ -70,7 +70,12 @@ 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 From b7cf8d898c821027da33958548fc2cce54b5d679 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 25 Nov 2021 15:06:56 +0100 Subject: [PATCH 419/424] Remove cleaning section --- docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md index 4df1f9dba..8a34ee72c 100644 --- a/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md +++ b/docs/upgrade/upgrade_zonemaster_backend_ver_8.0.0.md @@ -81,18 +81,6 @@ install -v -m 755 ./zm_rpcapi-bsd /usr/local/etc/rc.d/zm_rpcapi ``` -## Cleaning old files - -### Linux -```sh -sudo rm `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')`/patch_*.pl -``` - -### FreeBSD -```sh -rm `perl -MFile::ShareDir -le 'print File::ShareDir::dist_dir("Zonemaster-Backend")')`/patch_*.pl -``` - ## Upgrading the database If your Zonemaster database was created by a Zonemaster-Backend version smaller From a7dba7de5df36a9ce8516120e27020ca03a70539 Mon Sep 17 00:00:00 2001 From: Alexandre Pion Date: Thu, 25 Nov 2021 15:07:31 +0100 Subject: [PATCH 420/424] Uninstall Zonemaster::Backend when upgrading before installing --- docs/Upgrade.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index c00ad729b..6826a53e1 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -23,10 +23,11 @@ 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. install any new dependencies (see corresponding upgrade document) - 3. install the latest version from CPAN with `cpanm Zonemaster::Backend` - 4. apply any remaining instructions specific to this new release - 5. start the `zm-rpcapi` and `zm-testagent` daemons (`zm_rpcapi` and + 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) From 7bcd86f3832187161402c8ac50c148cd7cd715d6 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Mon, 29 Nov 2021 17:08:01 +0100 Subject: [PATCH 421/424] Adds Finnish translation --- share/fi.po | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/share/fi.po b/share/fi.po index a258d2e71..2e41f10af 100644 --- a/share/fi.po +++ b/share/fi.po @@ -2,12 +2,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" +"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" From f0f3c2e4ea7b580accdff404417dd6166ed45366 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 3 Dec 2021 17:12:47 +0100 Subject: [PATCH 422/424] Preparation for v2021.2 release * Bumps version in perl module * Updates Changes file * Updates requirement in Makefile.PL to latest version of Engine --- Changes | 38 ++++++++++++++++++++++++++++++++++++++ Makefile.PL | 2 +- lib/Zonemaster/Backend.pm | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 5750a76e5..21a05eaa0 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,43 @@ Release history for Zonemaster component Zonemaster-Backend +v8.0.0 2021-12-03 (public release version) + + [Features] + - Adds Finnish translation of error messages (#921, #911) + - Replaces CentOS with Rocky Linux (#908, #895) + - Adds upgrade documentation (#907, #879, #618) + - Adds Swedish translation of error messages (#899) + - Adds translation of error messages (#891) + - Drops support for PostgreSQL version 9 and before (#892) + - Adds support for collecting metrics (#844) + - Optimization of batches when PostgreSQL is used (#890) + - Improved result history response in API (#837, #830) + - Deprecates country code (#796, #794) + - 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) + - Removes "retry" (#896, 881) + - 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/Makefile.PL b/Makefile.PL index b52e4ecb3..fe902a07f 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -35,7 +35,7 @@ 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, 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; From 96dfc14f2eaeb0df567fabce30f38f58cb1c511b Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 3 Dec 2021 19:08:23 +0100 Subject: [PATCH 423/424] Updates * Explicit section for breaking changes. * Some rearrangement and grouping --- Changes | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Changes b/Changes index 21a05eaa0..bc9518c54 100644 --- a/Changes +++ b/Changes @@ -2,17 +2,20 @@ Release history for Zonemaster component Zonemaster-Backend v8.0.0 2021-12-03 (public release version) + [Breaking changes] + - Drops support for PostgreSQL version 9 and before (#892) + - Deprecates country code in RPC API calls (#796, #794) + - Removes "retry" configuration setting (#896, 881) + [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 Swedish translation of error messages (#899) - - Adds translation of error messages (#891) - - Drops support for PostgreSQL version 9 and before (#892) - Adds support for collecting metrics (#844) - Optimization of batches when PostgreSQL is used (#890) - Improved result history response in API (#837, #830) - - Deprecates country code (#796, #794) - Makes database port configurable (#755, #496) [Fixes] @@ -23,7 +26,6 @@ v8.0.0 2021-12-03 (public release version) #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) - - Removes "retry" (#896, 881) - 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, From 5bdb67e6d469b27154616b781fc2b6f9b341e937 Mon Sep 17 00:00:00 2001 From: Mats Dufberg Date: Fri, 3 Dec 2021 20:01:37 +0100 Subject: [PATCH 424/424] Editorial updates --- Changes | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index bc9518c54..bef41d545 100644 --- a/Changes +++ b/Changes @@ -3,10 +3,12 @@ Release history for Zonemaster component Zonemaster-Backend v8.0.0 2021-12-03 (public release version) [Breaking changes] - - Drops support for PostgreSQL version 9 and before (#892) - - Deprecates country code in RPC API calls (#796, #794) + - 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)