package Zonemaster::Engine::Constants; use v5.16.0; use warnings; use version; our $VERSION = version->declare("v1.2.5"); use Carp; use English qw( -no_match_vars ) ; use parent 'Exporter'; use Net::IP::XS; use Text::CSV; use File::ShareDir qw[dist_dir dist_file]; use Readonly; =head1 NAME Zonemaster::Engine::Constants - module holding constants used in Test modules =head1 SYNOPSIS use Zonemaster::Engine::Constants ':all'; =head1 EXPORTED GROUPS =over =item all All exportable names. =item algo DNSSEC algorithms. =item cname CNAME records. =item name Label and name lengths. =item ip IP version constants. =item soa SOA values limits. =item misc Other, uncategorized export names, e.g. UDP payload limit and minimum number of name servers per zone. =item addresses Address classes for IPv4 and IPv6. =back =cut our @EXPORT_OK = qw[ $ALGO_STATUS_DEPRECATED $ALGO_STATUS_PRIVATE $ALGO_STATUS_RESERVED $ALGO_STATUS_UNASSIGNED $ALGO_STATUS_OTHER $ALGO_STATUS_NOT_RECOMMENDED $ALGO_STATUS_NOT_ZONE_SIGN $BLACKLISTING_ENABLED $CNAME_MAX_CHAIN_LENGTH $CNAME_MAX_RECORDS $DURATION_5_MINUTES_IN_SECONDS $DURATION_1_HOUR_IN_SECONDS $DURATION_4_HOURS_IN_SECONDS $DURATION_12_HOURS_IN_SECONDS $DURATION_1_DAY_IN_SECONDS $DURATION_1_WEEK_IN_SECONDS $DURATION_180_DAYS_IN_SECONDS $FQDN_MAX_LENGTH $IP_VERSION_4 $IP_VERSION_6 $LABEL_MAX_LENGTH $SERIAL_BITS $SERIAL_MAX_VARIATION $MINIMUM_NUMBER_OF_NAMESERVERS $UDP_PAYLOAD_LIMIT $EDNS_UDP_PAYLOAD_DNSSEC_DEFAULT $EDNS_UDP_PAYLOAD_DEFAULT $EDNS_UDP_PAYLOAD_COMMON_LIMIT @IPV4_SPECIAL_ADDRESSES @IPV6_SPECIAL_ADDRESSES ]; our %EXPORT_TAGS = ( all => \@EXPORT_OK, algo => [ qw($ALGO_STATUS_DEPRECATED $ALGO_STATUS_PRIVATE $ALGO_STATUS_RESERVED $ALGO_STATUS_UNASSIGNED $ALGO_STATUS_OTHER $ALGO_STATUS_NOT_ZONE_SIGN $ALGO_STATUS_NOT_RECOMMENDED) ], cname => [ qw($CNAME_MAX_CHAIN_LENGTH $CNAME_MAX_RECORDS) ], name => [qw($FQDN_MAX_LENGTH $LABEL_MAX_LENGTH)], ip => [qw($IP_VERSION_4 $IP_VERSION_6)], soa => [ qw($DURATION_5_MINUTES_IN_SECONDS $DURATION_1_HOUR_IN_SECONDS $DURATION_4_HOURS_IN_SECONDS $DURATION_12_HOURS_IN_SECONDS $DURATION_1_DAY_IN_SECONDS $DURATION_1_WEEK_IN_SECONDS $DURATION_180_DAYS_IN_SECONDS $SERIAL_BITS $SERIAL_MAX_VARIATION) ], misc => [qw($UDP_PAYLOAD_LIMIT $EDNS_UDP_PAYLOAD_DNSSEC_DEFAULT $EDNS_UDP_PAYLOAD_DEFAULT $EDNS_UDP_PAYLOAD_COMMON_LIMIT $MINIMUM_NUMBER_OF_NAMESERVERS $BLACKLISTING_ENABLED)] , # everything in %EXPORT_OK that isn't included in any of the other tags addresses => [qw(@IPV4_SPECIAL_ADDRESSES @IPV6_SPECIAL_ADDRESSES)], ); =head1 EXPORTED NAMES =over =item * C<$ALGO_STATUS_DEPRECATED> =item * C<$ALGO_STATUS_PRIVATE> =item * C<$ALGO_STATUS_RESERVED> =item * C<$ALGO_STATUS_UNASSIGNED> =item * C<$ALGO_STATUS_OTHER> =item * C<$ALGO_STATUS_NOT_RECOMMENDED> =item * C<$ALGO_STATUS_NOT_ZONE_SIGN> =item * C<$BLACKLISTING_ENABLED> A boolean, used to enable the name server blacklisting mechanism. =item * C<$CNAME_MAX_CHAIN_LENGTH> An integer, used to define the maximum length of a CNAME chain when doing consecutive recursive lookups. =item * C<$CNAME_MAX_RECORDS> An integer, used to define the maximum number of CNAME records in a response. =item * C<$DURATION_5_MINUTES_IN_SECONDS> =item * C<$DURATION_1_HOUR_IN_SECONDS> =item * C<$DURATION_4_HOURS_IN_SECONDS> =item * C<$DURATION_12_HOURS_IN_SECONDS> =item * C<$DURATION_1_DAY_IN_SECONDS> =item * C<$DURATION_1_WEEK_IN_SECONDS> =item * C<$DURATION_180_DAYS_IN_SECONDS> =item * C<$FQDN_MAX_LENGTH> =item * C<$LABEL_MAX_LENGTH> =item * C<$IP_VERSION_4> =item * C<$IP_VERSION_6> =item * C<$SERIAL_BITS> An integer, used to define the size of the serial number space, as defined in RFC1982, section 2. =item * C<$SERIAL_MAX_VARIATION> =item * C<$MINIMUM_NUMBER_OF_NAMESERVERS> =item * C<$UDP_PAYLOAD_LIMIT> =item * C<$EDNS_UDP_PAYLOAD_DEFAULT> An integer, used to define the EDNS0 UDP packet size in non-DNSSEC EDNS queries. =item * C<$EDNS_UDP_PAYLOAD_COMMON_LIMIT> =item * C<$EDNS_UDP_PAYLOAD_DNSSEC_DEFAULT> An integer, used to define the EDNS0 UDP packet size in DNSSEC queries. =item * C<@IPV4_SPECIAL_ADDRESSES> =item * C<@IPV6_SPECIAL_ADDRESSES> =back =cut Readonly our $ALGO_STATUS_DEPRECATED => 1; Readonly our $ALGO_STATUS_PRIVATE => 4; Readonly our $ALGO_STATUS_RESERVED => 2; Readonly our $ALGO_STATUS_UNASSIGNED => 3; Readonly our $ALGO_STATUS_OTHER => 5; Readonly our $ALGO_STATUS_NOT_ZONE_SIGN => 8; Readonly our $ALGO_STATUS_NOT_RECOMMENDED => 9; Readonly our $BLACKLISTING_ENABLED => 1; Readonly our $CNAME_MAX_CHAIN_LENGTH => 10; Readonly our $CNAME_MAX_RECORDS => 9; Readonly our $DURATION_5_MINUTES_IN_SECONDS => 5 * 60; Readonly our $DURATION_1_HOUR_IN_SECONDS => 60 * 60; Readonly our $DURATION_4_HOURS_IN_SECONDS => 4 * 60 * 60; Readonly our $DURATION_12_HOURS_IN_SECONDS => 12 * 60 * 60; Readonly our $DURATION_1_DAY_IN_SECONDS => 24 * 60 * 60; Readonly our $DURATION_1_WEEK_IN_SECONDS => 7 * 24 * 60 * 60; Readonly our $DURATION_180_DAYS_IN_SECONDS => 180 * 24 * 60 * 60; # Maximum length of ASCII version of a domain name, with trailing dot. Readonly our $FQDN_MAX_LENGTH => 254; Readonly our $LABEL_MAX_LENGTH => 63; Readonly our $IP_VERSION_4 => 4; Readonly our $IP_VERSION_6 => 6; Readonly our $MINIMUM_NUMBER_OF_NAMESERVERS => 2; Readonly our $SERIAL_BITS => 32; Readonly our $SERIAL_MAX_VARIATION => 0; Readonly our $UDP_PAYLOAD_LIMIT => 512; Readonly our $EDNS_UDP_PAYLOAD_DEFAULT => 512; Readonly our $EDNS_UDP_PAYLOAD_COMMON_LIMIT => 4096; Readonly our $EDNS_UDP_PAYLOAD_DNSSEC_DEFAULT => 1232; Readonly::Array our @IPV4_SPECIAL_ADDRESSES => _extract_iana_ip_blocks($IP_VERSION_4); Readonly::Array our @IPV6_SPECIAL_ADDRESSES => _extract_iana_ip_blocks($IP_VERSION_6); =head1 METHODS =over =item _extract_iana_ip_blocks() my @array = _extract_iana_ip_blocks( $ip_version ); Internal method that is used to extract IP blocks details from IANA files for a given IP version (i.e. 4 or 6). Takes an integer (IP version). Returns a list of hashes - the keys of which are C (L object), C (string), C (string) and C (string). =back =cut sub _extract_iana_ip_blocks { my $ip_version = shift; my @list = (); my $csv = Text::CSV->new ({ binary => 1, auto_diag => 1, sep_char => q{,} }); my @files_details = ( { name => q{iana-ipv4-special-registry.csv}, ip_version => $IP_VERSION_4 }, { name => q{iana-ipv6-special-registry.csv}, ip_version => $IP_VERSION_6 }, ); foreach my $file_details ( @files_details ) { my $first_line = 1; next if ${$file_details}{ip_version} != $ip_version; my $makefile_name = 'Zonemaster-Engine'; # This must be the same name as "name" in Makefile.PL my $data_location = dist_file($makefile_name, ${$file_details}{name}); open(my $data, '<:encoding(utf8)', $data_location) or croak "Cannot open '${data_location}' : ${OS_ERROR}"; while (my $fields = $csv->getline( $data )) { if ( $first_line ) { $first_line = 0; next; } my $address_data = $fields->[0]; $address_data =~ s/[ ]+//smx; foreach my $address_item ( split /,/smx, $address_data ) { $address_item =~ s/(\A.+\/\d+).*\z/$1/smx; push @list, { ip => Net::IP::XS->new( $address_item ), name => $fields->[1], reference => $fields->[2], globally_reachable => $fields->[8] }; } } close $data or croak "Cannot close '${data_location}' : ${OS_ERROR}"; } return @list; } ## end sub _extract_iana_ip_blocks 1;