File Coverage

blib/lib/Net/ENUM.pm
Criterion Covered Total %
statement 64 67 95.5
branch 16 24 66.6
condition 4 7 57.1
subroutine 7 7 100.0
pod 5 5 100.0
total 96 110 87.2


line stmt bran cond sub pod time code
1             #
2             # Net::ENUM - E.164 NUmber Mapping
3             #
4             # This module is Copyright (C) 2010, Detlef Pilzecker.
5             # All Rights Reserved.
6             # This module is free software. It may be used, redistributed and/or modified
7             # under the same terms as Perl itself.
8             #
9              
10             package Net::ENUM;
11              
12 2     2   20672 use strict;
  2         4  
  2         72  
13 2     2   2071 use Net::DNS qw( rrsort );
  2         745264  
  2         1703  
14              
15             $Net::ENUM::VERSION = '0.3';
16              
17              
18             ################################################################################
19             # Args: ( $class, %args )
20             # $class: Net::ENUM
21             # %args: see Net::DNS::Resolver->new(%args), this can overwrite Net::ENUM
22             # default values! Example to use: udp_timeout => 5, ...
23             # Return: $self
24             ################################################################################
25             sub new {
26 1     1 1 18 my ( $class, $args, $vanity ) = @_;
27              
28 1 50 33     11 die '$args must be HASHREF' if $args && ref( $args ) ne 'HASH';
29              
30 1 50       4 if ( $vanity ) {
31 0 0       0 die '$vanity must be HASHREF' if ref( $vanity ) ne 'HASH';
32             }
33             else {
34 1         12 $vanity = {
35             'abc' => 2, 'def' => 3, 'ghi' => 4, 'jkl' => 5,
36             'mno' => 6, 'pqrs' => 7, 'tuv' => 8, 'wxyz' => 9,
37             };
38             }
39              
40 1         7 my $self = {
41             'enum_error' => '',
42             'res_args' => $args,
43             'vanity' => $vanity,
44             };
45              
46 1         3 bless( $self, $class );
47              
48 1         4 return $self;
49             }
50              
51              
52             ################################################################################
53             # Args: ( $self, $number, $sortby, $media )
54             # $number: Phone number in format: +123 456-789 or 9.8.7.6.5.4.3.2.1.e164.arpa
55             # $sortby: sort based on the attribute (must be of type NAPTR), defaults to
56             # 'order' (= lowest to highest order, for same order lowest preference first)
57             # $media: NAPTR media to return (sip, tel, email, ...), default: all
58             # Return:
59             # if array is wanted: array with all sorted NAPTR entries in hashrefs
60             # keys in the hashref: flags, ttl ,name, service, rdata, preference,
61             # rdlength, regexp, order, type, class, replacement
62             # if string is wanted: string with contact of first NAPTR entry (after sorting)
63             # the RegEx is already done!
64             # on error
65             ################################################################################
66             sub get_enum_address {
67 2     2 1 2121 my Net::ENUM $self = shift;
68 2 50       10 my $e164 = $self->number_to_domain( shift ) or return;
69 2   100     11 my $sortby = shift || 'order';
70 2         5 my $media = shift;
71              
72 2 50       7 my $nameservers = $self->get_nameservers( $e164 ) or return;
73              
74 2         13 my $res = Net::DNS::Resolver->new(
75             nameservers => $nameservers,
76             recurse => 0,
77 2         90 %{ $self->{'res_args'} }
78             );
79              
80 2         12496 my $NAPTR = $res->query( $e164, 'NAPTR' );
81              
82 2 50       59582 if ( $NAPTR ) {
83 2         12 my @rr_array = ( $NAPTR->answer );
84              
85 2         24 my @sorted = rrsort( 'NAPTR', $sortby, @rr_array );
86 2 100       190 @sorted = grep { $_->{'service'} =~ /$media/ } @sorted if $media;
  3         23  
87              
88 2 100       29 if ( wantarray ) {
89 1         5 $self->{'enum_error'} = '';
90 1         39 return @sorted;
91             }
92             else {
93 1         4 $sorted[0]->{'regexp'} =~ /^(.)(.+)\1(.+)\1/;
94 1         4 my ( $pattern, $replace ) = ( $2, $3 );
95 1         9 $e164 =~ s/$pattern/$replace/;
96              
97 1         4 $self->{'enum_error'} = '';
98 1         58 return $e164;
99             }
100             }
101             else {
102 0         0 $self->{'enum_error'} = "'NAPTR' query failed: " . $res->errorstring . "\n";
103 0         0 return;
104             }
105             }
106              
107              
108             ################################################################################
109             # Args: ( $self, $domain )
110             # $domain: domain in format: 9.8.7.6.5.4.3.2.1.e164.arpa
111             # Return:
112             # arrayref with nameservers, on error
113             ################################################################################
114             sub get_nameservers {
115 4     4 1 1409 my Net::ENUM $self = shift;
116 4   50     14 my $e164 = shift || return;
117              
118 4         6 my $res = Net::DNS::Resolver->new( %{ $self->{'res_args'} } );
  4         53  
119              
120 4         774 my $query = $res->query( $e164, 'NS' );
121              
122 4 100       19193 if ( $query ) {
123 3         7 $self->{'enum_error'} = '';
124 3         10 return [ map { $_->nsdname } grep { $_->type eq 'NS' } $query->answer ];
  9         273  
  9         79  
125             }
126             else {
127 1         4 $self->{'enum_error'} = "Nameservers query failed: " . $res->errorstring . "\n";
128 1         21 return;
129             }
130             }
131              
132              
133             ################################################################################
134             # Args: ( $self, $number )
135             # $number: Phone number in format: +123 456-789 or 9.8.7.6.5.4.3.2.1.e164.arpa
136             # Return:
137             # 9.8.7.6.5.4.3.2.1.e164.arpa, on error
138             ################################################################################
139             sub number_to_domain {
140 6     6 1 2673 my Net::ENUM $self = shift;
141 6         14 my $number = lc( shift );
142              
143 6 50       26 return $number if $number =~ /^(?:\d\.)+e164\.arpa$/;
144              
145 6 100       31 unless ( $number =~ /^\s*\+/ ) {
146 1         8 $self->{'enum_error'} = "Phone number ($number) must begin with '+'.\n";
147 1         3 return;
148             }
149              
150 5         18 $self->translate_vanity( $number );
151              
152 5         30 $number =~ s/[^\d]//g;
153              
154 5 100       13 unless ( $number ) {
155 1         3 $self->{'enum_error'} = "Phone number must contain numbers!\n";
156 1         3 return;
157             }
158              
159 4         9 $self->{'enum_error'} = '';
160 4         32 return reverse( join( '.', split( //, $number ) ) ) . '.e164.arpa';
161             }
162              
163              
164             ################################################################################
165             # Args: ( $self, $number )
166             # $number: Phone number, can contain letters (vanity) they will be translatent
167             # into numbers here
168             # Return:
169             # translates the $number in place, but you can also use the returned
170             ################################################################################
171             sub translate_vanity {
172 5     5 1 9 my Net::ENUM $self = $_[0];
173              
174 5         6 map { $_[1] =~ s/[$_]/$self->{'vanity'}{ $_ }/gi } keys %{ $self->{'vanity'} };
  40         476  
  5         23  
175              
176 5         12 return $_[1];
177             }
178              
179             1;