File Coverage

blib/lib/Data/Validate/Domain.pm
Criterion Covered Total %
statement 63 64 98.4
branch 46 48 95.8
condition 6 9 66.6
subroutine 10 10 100.0
pod 4 4 100.0
total 129 135 95.5


line stmt bran cond sub pod time code
1             package Data::Validate::Domain;
2              
3 1     1   112521 use strict;
  1         11  
  1         30  
4 1     1   5 use warnings;
  1         2  
  1         58  
5              
6             our $VERSION = '0.15';
7              
8 1     1   856 use Net::Domain::TLD 1.74 qw(tld_exists);
  1         12637  
  1         127  
9              
10 1     1   8 use Exporter qw( import );
  1         3  
  1         763  
11              
12             ## no critic (Modules::ProhibitAutomaticExportation)
13             our @EXPORT = qw(
14             is_domain
15             is_hostname
16             is_domain_label
17             );
18              
19             sub new {
20 5     5 1 12 my $class = shift;
21              
22 5   33     31 return bless {@_}, ref($class) || $class;
23             }
24              
25             # -------------------------------------------------------------------------------
26              
27             sub is_domain {
28 61     61 1 17571 my ( $value, $opt ) = _maybe_oo(@_);
29              
30 61         151 my ( $hostname, $bits ) = _domain_labels( $value, $opt );
31              
32 61 100       170 return unless $bits;
33              
34 45         86 my $tld = $bits->[-1];
35              
36             # domain_allow_single_label set to true disables this check
37 45 100       90 unless ( $opt->{domain_allow_single_label} ) {
38              
39             # All domains have more then 1 label (neely.cx good, com not good)
40 40 100       49 return if @{$bits} < 2;
  40         115  
41             }
42              
43 36 100       79 return $hostname if $opt->{domain_disable_tld_validation};
44              
45             # If the option to enable domain_private_tld is enabled
46             # and a private domain is specified, then we return if that matches
47 35 100 66     101 if ( exists $opt->{domain_private_tld}
48             && ref( $opt->{domain_private_tld} ) ) {
49 13         29 my $lc_tld = lc($tld);
50 13 100       33 if ( ref( $opt->{domain_private_tld} ) eq 'HASH' ) {
51 8 100       19 if ( exists $opt->{domain_private_tld}->{$lc_tld} ) {
52 6         38 return $hostname;
53             }
54             }
55             else {
56 5 100       30 if ( $tld =~ $opt->{domain_private_tld} ) {
57 3         27 return $hostname;
58             }
59             }
60             }
61              
62             # Verify domain has a valid TLD
63 26 100       74 return unless tld_exists($tld);
64              
65 19         241 return $hostname;
66             }
67              
68             # -------------------------------------------------------------------------------
69              
70             sub is_hostname {
71 11     11 1 4207 my ( $value, $opt ) = _maybe_oo(@_);
72              
73 11         27 my ($hostname) = _domain_labels( $value, $opt );
74              
75             # We do not verify TLD for hostnames, as hostname.subhost is a valid hostname
76              
77 11         53 return $hostname;
78             }
79              
80             sub _domain_labels {
81 72     72   131 my ( $value, $opt ) = @_;
82              
83 72 100       163 return unless defined($value);
84              
85             # FYI: DNS limits names to 255 octets, encoded to RDATA. Each label
86             # includes a length-octet prefix; those length octets count against the
87             # 255-octet maximum. The number of labels exceeds the number of dots by 1
88             # (assuming no trailing dot), and the number of length octets exceeds
89             # number of labels by 1 (since there’s always a trailing NUL octet). The
90             # effective limit is thus 255 - 1 - 1, or 253. See
91             # https://devblogs.microsoft.com/oldnewthing/20120412-00/?p=7873 for a
92             # more detailed explanation of this.
93              
94 70 100       210 my $trailing_dot = $value =~ s/\.\z// ? q{.} : q{};
95              
96 70 100       149 my $encoded_length = ( $trailing_dot ? 1 : 2 ) + length($value);
97              
98 70 100       137 return if $encoded_length > 255;
99              
100 66         99 my @bits;
101 66         224 foreach my $label ( split /\./, $value, -1 ) {
102 116         218 my $bit = is_domain_label( $label, $opt );
103 116 100       247 return unless defined $bit;
104 104         224 push( @bits, $bit );
105             }
106              
107 54 100       135 return unless @bits;
108              
109 51         204 return ( join( '.', @bits ) . $trailing_dot, \@bits );
110             }
111              
112             sub is_domain_label {
113 128     128 1 6040 my ( $value, $opt ) = _maybe_oo(@_);
114              
115 128 100       270 return unless defined($value);
116              
117             # Fix Bug: 41033
118 127 100       281 return if ( $value =~ /\n/ );
119              
120             # bail if we are dealing with more then just a hostname
121 126 50       232 return if ( $value =~ /\./ );
122 126         182 my $length = length($value);
123 126         157 my $hostname;
124 126 100 100     418 if ( $length == 1 ) {
    100          
125 8 50       19 if ( $opt->{domain_allow_underscore} ) {
126 0         0 ($hostname) = $value =~ /^([0-9A-Za-z\_])$/;
127             }
128             else {
129 8         25 ($hostname) = $value =~ /^([0-9A-Za-z])$/;
130             }
131             }
132             elsif ( $length > 1 && $length <= 63 ) {
133 114 100       232 if ( $opt->{domain_allow_underscore} ) {
134 9         39 ($hostname)
135             = $value =~ /^([0-9A-Za-z\_][0-9A-Za-z\-\_]*[0-9A-Za-z])$/;
136             }
137             else {
138 105         476 ($hostname)
139             = $value =~ /^([0-9A-Za-z][0-9A-Za-z\-]*[0-9A-Za-z])$/;
140             }
141             }
142             else {
143 4         13 return;
144             }
145 122         309 return $hostname;
146             }
147              
148             sub _maybe_oo {
149 200 100   200   400 if ( ref $_[0] ) {
150 15         42 return @_[ 1, 0 ];
151             }
152             else {
153 185 100       528 return ( $_[0], ( defined $_[1] ? $_[1] : {} ) );
154             }
155             }
156              
157             1;
158              
159             # ABSTRACT: Domain and host name validation
160              
161             __END__