File Coverage

blib/lib/Validate/Net.pm
Criterion Covered Total %
statement 62 80 77.5
branch 27 44 61.3
condition 3 8 37.5
subroutine 11 15 73.3
pod 4 10 40.0
total 107 157 68.1


line stmt bran cond sub pod time code
1             package Validate::Net;
2              
3             # Validate::Net is designed to allow you to test net related string to
4             # determine their relative "validity".
5              
6             # We use Class::Default to allow us to create a "default" validator
7             # which has a "medium" setting. Settings are discussed later.
8              
9 2     2   33583 use 5.005;
  2         8  
  2         77  
10 2     2   11 use strict;
  2         3  
  2         147  
11 2     2   22 use base 'Class::Default';
  2         4  
  2         1998  
12              
13             # Globals
14 2     2   908 use vars qw{$VERSION $errstr $reason};
  2         5  
  2         126  
15             BEGIN {
16 2     2   4 $VERSION = '0.6';
17 2         3 $errstr = '';
18 2         2628 $reason = ''
19             }
20              
21              
22              
23              
24              
25             #####################################################################
26             # Constructor and Friends
27              
28             sub new {
29 1     1 0 17 my $class = shift;
30 1   50     7 my $depth = shift || 'local';
31              
32             # Create the validtor object
33 1         6 my $self = bless {
34             depth => undef,
35             }, $class;
36              
37             # Set the depth
38 1 50       6 $self->depth( $depth ) or return undef;
39 1         5 $self;
40             }
41              
42             sub depth {
43 1     1 0 2 my $self = shift;
44 1 50       3 unless ( ref $self ) {
45 0         0 return $self->andError( "Cannot change the depth of the default object. You should instantiate instead" );
46             }
47              
48 1         3 my $depth = shift;
49 1 50       4 return $self->{depth} unless defined $depth;
50 1 50 33     9 unless ( $depth eq 'fast' or $depth eq 'local' or $depth eq 'full' ) {
      33        
51 0         0 return $self->andError( "Invalid depth '$depth'. Valid depths are 'fast', 'local'(default) or 'full'" );
52             }
53 1         7 $self->{depth} = $depth;
54 1         5 1;
55             }
56              
57              
58              
59              
60              
61             #####################################################################
62             # Testing
63              
64             # Validate an ip address
65             sub ip {
66 5     5 1 868 my $self = shift->_self;
67 5 50       55 my $ip = shift or return undef;
68              
69             # Clear the reason
70 5         7 $reason = '';
71              
72             # First, do a basic character test.
73             # Just what we can get away with in a regex.
74 5 100       33 unless ( $ip =~ /^[0-9]\d{0,2}(?:\.[0-9]\d{0,2}){3}$/ ) {
75 1         5 return $self->withReason( 'Does not fit the basic dotted quad format for an ip' );
76             }
77              
78             # Split into parts in preperation for the remaining tests
79 4         17 my @quad = split /\./, $ip;
80              
81             # Make sure the basic numeric range is ok
82 4 50       9 if ( scalar grep { $_ > 255 } @quad ) {
  16         41  
83 0         0 return $self->withReason( 'The maximum value for an ip element is 255' );
84             }
85              
86             # End of the fast tests
87 4 50       11 return 1 if $self->{depth} eq 'fast';
88              
89             ### Add tests for options
90              
91 4         24 1;
92             }
93              
94             # Validate a full or partial domain name, or just a host name
95             sub domain {
96 28     28 1 5990 my $self = shift->_self;
97 28 50       185 my $domain = lc shift or return undef;
98              
99             # Do a quick check for any invalid characters, or basic problems
100 28 50       84 if ( $domain =~ /[^a-z0-9\.-]/ ) {
101 0         0 return $self->withReason( "Domain '$domain' contains invalid characters" );
102             }
103 28 50       58 if ( $domain =~ /\.\./ ) {
104 0         0 return $self->withReason( "Domain '$domain' contains consecutive dots" );
105             }
106 28 100       64 if ( $domain =~ /^\./ ) {
107 2         8 return $self->withReason( "Domain '$domain' cannot start with a dot" );
108             }
109              
110             # The use of a trailing dot is allowed, but we remove it for testing purposes.
111 26         33 $domain =~ s/\.$//;
112              
113             # Split into elements
114 26         85 my @elements = split /\./, $domain;
115              
116             # Check each element individually
117 26         46 foreach my $element ( @elements ) {
118             # Segments can be no more than 63 characters
119 40 100       76 if ( length $element > 63 ) {
120 2         9 return $self->withReason( "Domain section '$element' cannot be longer than 63 characters" );
121             }
122              
123             # Segments are allowed to contain only digits
124 38 100       92 next if $element =~ /^\d+$/;
125              
126             # Segment must start with a letter
127 34 100       91 if ( $element !~ /^[a-z]/ ) {
128 6         25 return $self->withReason( "Domain section '$element' must start with a letter" );
129             }
130              
131             # Segment must end with a letter or number
132 28 100       83 if ( $element !~ /[a-z0-9]$/ ) {
133 4         18 return $self->withReason( "Domain section '$element' must end with a letter or number" );
134             }
135              
136             # Cannot have two consecutive dashes ( RFC doesn't say so that I can find... is this correct? )
137 24 100       67 if ( $element =~ /--/ ) {
138 2         11 return $self->withReason( "Domain sections '$element' cannot have two dashes in a row" );
139             }
140             }
141              
142 12 50       35 return 1 if $self->{depth} eq 'fast';
143              
144             ### Add tests for options
145              
146 12         88 1;
147             }
148              
149             # Validate a host.
150             # A host is EITHER an ip address, or a domain
151             sub host {
152 16     16 1 52 my $self = shift->_self;
153 16         91 my $host = shift;
154              
155             # Test as an ip or a domain
156 16 100       62 $host =~ /^\d+\.\d+\.\d+\.\d+$/
157             ? $self->ip( $host )
158             : $self->domain( $host );
159             }
160              
161             # Validate a port number
162             sub port {
163 0     0 1 0 my $self = shift->_self;
164 0         0 my $port = shift;
165              
166             # A port must be all numbers
167 0 0       0 if ( $port =~ /[^0-9]/ ) {
168 0         0 return $self->withReason( 'A port number must be an integer' );
169             }
170              
171             # A port cannot start with 0
172 0 0       0 if ( $port =~ /^0/ ) {
173 0         0 return $self->withReason( 'A port number cannot start with zero' );
174             }
175              
176             # A port must be less than or equal to 65535
177 0 0       0 if ( $port > 65535 ) {
178 0         0 return $self->withReason( 'The port number is too high' );
179             }
180              
181             # Otherwise OK
182 0         0 1;
183             }
184              
185              
186              
187              
188             #####################################################################
189             # Error and Message Handling
190              
191 0     0 0 0 sub andError { $errstr = $_[1]; undef }
  0         0  
192 17     17 0 24 sub withReason { $reason = $_[1]; '' }
  17         91  
193 0     0 0   sub errstr { $errstr }
194 0     0 0   sub reason { $reason }
195              
196             1;
197              
198             __END__