File Coverage

blib/lib/Net/DRI.pm
Criterion Covered Total %
statement 143 183 78.1
branch 43 92 46.7
condition 10 24 41.6
subroutine 22 30 73.3
pod 1 14 7.1
total 219 343 63.8


line stmt bran cond sub pod time code
1             ## Domain Registry Interface, Main entry point
2             ##
3             ## Copyright (c) 2005-2015 Patrick Mevzek . All rights reserved.
4             ##
5             ## This file is part of Net::DRI
6             ##
7             ## Net::DRI is free software; you can redistribute it and/or modify
8             ## it under the terms of the GNU General Public License as published by
9             ## the Free Software Foundation; either version 2 of the License, or
10             ## (at your option) any later version.
11             ##
12             ## See the LICENSE file that comes with this distribution for more details.
13             ####################################################################################################
14              
15             package Net::DRI;
16              
17 66     66   175364 use strict;
  66         104  
  66         2322  
18 66     66   267 use warnings;
  66         82  
  66         1688  
19              
20 66     66   23698 use Net::DRI::Cache;
  66         180  
  66         592  
21 66     66   36415 use Net::DRI::Registry;
  66         172  
  66         521  
22 66     66   2578 use Net::DRI::Util;
  66         105  
  66         1232  
23 66     66   268 use Net::DRI::Exception;
  66         96  
  66         1314  
24              
25 66     66   273 use base qw(Class::Accessor::Chained::Fast Net::DRI::BaseClass);
  66         100  
  66         103030  
26             __PACKAGE__->mk_ro_accessors(qw/trid_factory logging cache/);
27              
28             our $AUTOLOAD;
29             our $VERSION='0.96_07';
30              
31             =pod
32              
33             =head1 NAME
34              
35             Net::DRI - Interface to Domain Name Registries/Registrars/Resellers
36              
37             =head1 VERSION
38              
39             This documentation refers to Net::DRI version 0.96_05
40              
41             =head1 SYNOPSIS
42              
43             use Net::DRI;
44             my $dri=Net::DRI->new({ cache_ttl => 10, trid_factory => ..., logging => .... });
45              
46             ... various operations ...
47              
48             $dri->end();
49              
50             =head1 DESCRIPTION
51              
52             Net::DRI is a Perl library to access services offered by domain name
53             providers, such as registries or registrars. DRI stands for
54             Domain Registration Interface and it aims to be
55             for domain name registries/registrars/resellers what DBI is for databases:
56             an abstraction over multiple providers, with multiple policies, transports
57             and protocols all used through a uniform API.
58              
59             It is an object-oriented framework implementing RRP (RFC 2832/3632),
60             EPP (core EPP in RFC 5730/5731/5732/5733/5734 aka STD69, extensions in
61             RFC 3915/4114/4310/5076 and various extensions of ccTLDs/gTLDs
62             - currently more than 60 TLDs are directly supported with extensions),
63             RRI (.DE registration protocol), Whois, DAS (Domain Availability Service used by .BE, .EU, .AU, .NL),
64             IRIS (RFC3981) DCHK (RFC5144) over LWZ (RFC4993) for .DE currently and XCP (RFC4992),
65             .FR/.RE email and webservices interface, and resellers interface of some registrars
66             (Gandi, OpenSRS, etc.).
67             It has transports for connecting with UDP/TCP/TLS, HTTP/HTTPS,
68             Web Services (XML-RPC and SOAP with/without WSDL),
69             or SMTP-based registries/registrars.
70              
71             It is not limited to handling of domain names, it can be easily extended.
72             For example, it supports ENUM registrations and validations, or DNSSEC provisioning.
73              
74             A shell is included for easy prototyping and debugging, see L.
75             Caching and logging features are also included by default.
76              
77             Please see the included README file for full details.
78              
79             =head1 EXAMPLES
80              
81             Please see the C subdirectory of the distribution, it contains various
82             examples. Please also see all unit tests under C, they show all parts of the API.
83              
84             =head1 SUBROUTINES/METHODS
85              
86             After having used Net::DRI (which is the only module you should need to C from
87             this distribution), you create an object as instance of this class,
88             and every operation will be carried through it.
89              
90             =head2 trid_factory()
91              
92             This is an accessor to the trid factory (code reference) used to generate client
93             transaction identificators, that are useful for logging and asynchronous operations.
94              
95             During the C call, a C is initialized to a default safe value
96             (being Net::DRI::Util::create_trid_1).
97              
98             You need to call this method only if you wish to use another function to generate transaction identificators.
99              
100             All other objects (registry profiles and transports)
101             created after that will inherit this value. If you call again C
102             the change will only apply to new objects (registry profiles and transports) created after the change,
103             it will not apply to already existing objects (registry profiles and transports).
104              
105             =head2 logging()
106              
107             This is an accessor to the underlying Logging object. During the C call you can
108             provide the object, or just a string ("null", "stderr", "files" or "syslog" which are the
109             current logging modules available in Net::DRI), or a reference to an array
110             with the first parameter a string (same as previously) and the second parameter a reference to
111             an hash with data needed by the logging class used (see for example L).
112              
113             If you want to log the application data (what is exchanged with remote server, such as EPP XML streams),
114             you need to use logging level of 'notice', or higher.
115              
116             =head2 cache()
117              
118             This is an accessor to the underlying Cache object. See L.
119             This object has a C method to access and change the current time to live
120             for cached data.
121              
122             =head1 SUPPORT
123              
124             For now, support questions should be sent to:
125              
126             Enetdri@dotandco.comE
127              
128             Please also see the SUPPORT file in the distribution.
129              
130             =head1 SEE ALSO
131              
132             L
133              
134             =head1 AUTHOR
135              
136             Patrick Mevzek, Enetdri@dotandco.comE
137             and various contributors (see Changes file and web page above)
138              
139             =head1 COPYRIGHT
140              
141             Copyright (c) 2005-2015 Patrick Mevzek .
142             All rights reserved.
143              
144             =head1 LICENSE
145              
146             This program is free software; you can redistribute it and/or modify
147             it under the terms of the GNU General Public License as published by
148             the Free Software Foundation; either version 2 of the License, or
149             (at your option) any later version.
150              
151             See the LICENSE file that comes with this distribution for more details.
152              
153             =cut
154              
155             ####################################################################################################
156              
157             sub new
158             {
159 59     59 1 140489 my ($class,$rh)=@_;
160              
161 59 100 66     398 my $self={ cache => Net::DRI::Cache->new(Net::DRI::Util::has_key($rh,'cache_ttl') ? $rh->{cache_ttl} : 0),
    100          
162             current_registry => undef, ## registry name (key of following hash)
163             registries => {}, ## registry name => Net::DRI::Registry object
164             tlds => {}, ## tld => [ registries name ]
165             time_created => time(),
166             trid_factory => (Net::DRI::Util::has_key($rh,'trid_factory') && ref $rh->{trid_factory} eq 'CODE')? $rh->{trid_factory} : \&Net::DRI::Util::create_trid_1,
167             };
168              
169 59         130 my ($logname,@logdata);
170 59 100       2257 if (Net::DRI::Util::has_key($rh,'logging'))
171             {
172 5 50       30 ($logname,@logdata)=ref $rh->{logging} eq 'ARRAY' ? @{$rh->{logging}} : ($rh->{logging});
  0         0  
173             } else
174             {
175 54         126 $logname='null';
176             }
177 59 50       299 if ($logname !~ s/^\+//) { $logname='Net::DRI::Logging::'.ucfirst($logname); }
  59         256  
178 59         262 Net::DRI::Util::load_module($logname,'DRI');
179 59         515 $self->{logging}=$logname->new(@logdata);
180              
181 59         187 bless($self,$class);
182 59         439 $self->logging()->setup_channel(__PACKAGE__,'core');
183 59         674 $self->log_output('notice','core','Successfully created Net::DRI object with logging='.$logname);
184 59         221 return $self;
185             }
186              
187             sub add_current_registry
188             {
189 0     0 0 0 my ($self,@p)=@_;
190 0         0 $self->add_registry(@p);
191 0         0 $self->target($self->{last_registry});
192 0         0 return $self;
193             }
194              
195             sub add_registry
196             {
197 59     59 0 273 my ($self,$reg,@data)=@_;
198 59 50       318 Net::DRI::Exception::usererr_insufficient_parameters('add_registry needs a registry name') unless Net::DRI::Util::all_valid($reg);
199 59 50       306 $reg='Net::DRI::DRD::'.$reg unless $reg=~m/^\+/;
200 59         222 Net::DRI::Util::load_module($reg,'DRI');
201              
202 57         498 my $drd=$reg->new(@data);
203 57 50 33     489 Net::DRI::Exception->die(1,'DRI',9,'Failed to initialize registry '.$reg) unless ($drd && ref $drd);
204              
205 57 50       582 Net::DRI::Exception::method_not_implemented('name',$reg) unless $drd->can('name');
206 57         222 my $regname=$drd->name();
207 57 50       372 Net::DRI::Exception->die(1,'DRI',10,'No dot allowed in registry name: '.$regname) unless index($regname,'.') == -1;
208 57 50       296 Net::DRI::Exception->die(1,'DRI',11,'New registry name already in use') if exists $self->{registries}->{$regname};
209              
210 57         620 my $ndr=Net::DRI::Registry->new($regname,$drd,$self->{cache},$self->{trid_factory},$self->{logging});
211 57 50       329 Net::DRI::Exception::method_not_implemented('tlds',$reg) unless $drd->can('tlds');
212 57         239 foreach my $tld ($drd->tlds())
213             {
214 475         529 $tld=lc($tld);
215 475 50       1513 $self->{tlds}->{$tld}=[] unless exists $self->{tlds}->{$tld};
216 475         397 push @{$self->{tlds}->{$tld}},$regname;
  475         800  
217             }
218              
219 57         474 $self->log_output('notice','core','Successfully added registry "'.$regname.'"');
220 57         238 $self->{registries}->{$regname}=$ndr;
221 57         323 $self->{last_registry}=$regname;
222 57         198 return $self;
223             }
224              
225             sub del_registry
226             {
227 0     0 0 0 my ($self,$name)=@_;
228 0 0       0 if (defined $name)
229             {
230 0 0       0 err_registry_name_does_not_exist($name) unless exists $self->{registries}->{$name};
231             } else
232             {
233 0 0       0 err_no_current_registry() unless defined $self->{current_registry};
234 0         0 $name=$self->{current_registry};
235             }
236 0         0 $self->{registries}->{$name}->end();
237 0         0 delete($self->{registries}->{$name});
238 0 0       0 $self->{current_registry}=undef if $self->{current_registry} eq $name;
239 0         0 $self->log_output('notice','core','Successfully deleted registry "'.$name.'"');
240 0         0 return $self;
241             }
242              
243             ####################################################################################################
244              
245 0     0 0 0 sub err_no_current_registry { Net::DRI::Exception->die(0,'DRI',1,'No current registry available'); } ## no critic (Subroutines::RequireArgUnpacking Subroutines::RequireFinalReturn)
246 0     0 0 0 sub err_registry_name_does_not_exist { Net::DRI::Exception->die(0,'DRI',2,'Registry name '.$_[0].' does not exist'); } ## no critic (Subroutines::RequireArgUnpacking Subroutines::RequireFinalReturn)
247              
248             ####################################################################################################
249             ## Accessor functions
250              
251 0     0 0 0 sub available_registries { my ($self)=@_; my @r=sort { $a cmp $b } keys %{$self->{registries}}; return @r; }
  0         0  
  0         0  
  0         0  
  0         0  
252             sub available_registries_profiles
253             {
254 0     0 0 0 my ($self,$full)=@_;
255 0         0 my %r;
256 0         0 foreach my $reg (keys(%{$self->{registries}}))
  0         0  
257             {
258 0         0 $r{$reg}=[ $self->{registries}->{$reg}->available_profiles($full) ];
259             }
260 0         0 return \%r;
261             }
262 99     99 0 251 sub registry_name { return shift->{current_registry}; }
263              
264             sub registry
265             {
266 99     99 0 163 my ($self)=@_;
267 99         392 my $regname=$self->registry_name();
268 99 50 33     601 err_no_current_registry() unless (defined($regname) && $regname);
269 99 50       351 err_registry_name_does_not_exist($regname) unless (exists($self->{registries}->{$regname}));
270 99         217 my $ndr=$self->{registries}->{$regname};
271 99 50       451 return wantarray? ($regname,$ndr) : $ndr;
272             }
273              
274             sub tld2reg
275             {
276 12     12 0 26 my ($self,$tld)=@_;
277 12 50 33     73 return unless defined($tld) && $tld;
278 12         39 $tld=lc($tld);
279 12 50       179 $tld=$1 if ($tld=~m/\.([a-z0-9]+)$/);
280 12 50       65 return unless exists($self->{tlds}->{$tld});
281 12         20 my @t=@{$self->{tlds}->{$tld}};
  12         40  
282 12         33 return @t;
283             }
284              
285             sub installed_registries
286             {
287 0     0 0 0 return qw/AdamsNames AERO AFNIC AG ARNES ASIA AT AU BE BIZ BookMyName BR BZ CAT CentralNic CIRA CoCCA COOP COZA CZ DENIC EURid Gandi GL HN ICMRegistry ID IENUMAT IM INFO IRegistry ISPAPI IT LC LU ME MN MOBI NAME Nominet NO NU OpenSRS ORG OVH PL PRO PT SC SE SIDN SO SWITCH TCI Telnic TRAVEL UPU US VC VNDS WS/;
288             }
289              
290             ####################################################################################################
291              
292             sub target
293             {
294 57     57 0 180 my ($self,$driver,$profile)=@_;
295              
296             ## Try to convert if given a domain name or a tld instead of a driver's name
297 57 100 66     551 if (defined $driver && ! exists $self->{registries}->{$driver})
298             {
299 12         73 my @t=$self->tld2reg($driver);
300 12 50       74 Net::DRI::Exception->die(0,'DRI',7,'Registry not found for domain name/TLD '.$driver) unless (@t==1);
301 12         29 $driver=$t[0];
302             }
303              
304 57 50       240 $driver=$self->registry_name() unless defined $driver;
305 57 50 33     496 err_registry_name_does_not_exist($driver) unless defined $driver && $driver;
306              
307 57 50       423 if (defined $profile)
308             {
309 0         0 $self->{registries}->{$driver}->target($profile);
310             }
311              
312 57         195 $self->{current_registry}=$driver;
313 57         364 return $self;
314             }
315              
316             ####################################################################################################
317             ## The meat of everything
318             ## See Cookbook, page 468
319             sub AUTOLOAD
320             {
321 99     99   645 my ($self,@args)=@_;
322 99         164 my $attr=$AUTOLOAD;
323 99         1164 $attr=~s/.*:://;
324 99 50       631 return unless $attr=~m/[^A-Z]/; ## skip DESTROY and all-cap methods
325              
326 99         344 my ($name,$ndr)=$self->registry();
327 99 50 33     693 Net::DRI::Exception::method_not_implemented($attr,$ndr) unless ref $ndr && $ndr->can($attr);
328 99         673 $self->log_output('debug','core','Calling '.$attr.' from Net::DRI');
329 99         520 return $ndr->$attr(@args); ## is goto beter here ?
330             }
331              
332             sub end
333             {
334 59     59 0 125 my $self=shift;
335 59         115 while(my ($name,$v)=each(%{$self->{registries}}))
  116         2357  
336             {
337 57 50 33     466 $v->end() if (ref($v) && $v->can('end'));
338 57         480 $self->log_output('notice','core','Successfully ended registry "'.$name.'"');
339 57         179 $v={};
340             }
341 59         183 $self->{tlds}={};
342 59         423 $self->{registries}={};
343 59         1237 $self->{current_registry}=undef;
344 59 50       261 if (defined $self->{logging})
345             {
346 59         253 $self->log_output('notice','core','Successfully ended Net::DRI object');
347 59         126 $self->{logging}=undef;
348             }
349              
350 59         1230 return 1; ## this makes it easy to test if everything before was ok or not, if we are inside an eval {} and $dri->end() is the last operation inside the eval block
351             }
352              
353 59     59   1515 sub DESTROY { my $self=shift; return $self->end(); }
  59         277  
354              
355             ####################################################################################################
356              
357             package Net::DRI::TrapExceptions; ## no critic (Modules::ProhibitMultiplePackages)
358              
359 66     66   447 use base qw/Net::DRI/;
  66         107  
  66         29128  
360              
361             our $AUTOLOAD;
362              
363             ## Some methods may die in Net::DRI, we specifically trap them
364 48 100   48   473 sub add_registry { my ($self,@p)=@_; my $r; my $ok=eval { $r=$self->SUPER::add_registry(@p); 1; }; return $r if $ok; die(ref $@ ? $@->as_string() : $@); }
  48 50       75  
  48         70  
  48         291  
  46         117  
  48         542  
  2         11  
365 0 0   0   0 sub del_registry { my ($self,@p)=@_; my $r; my $ok=eval { $r=$self->SUPER::del_registry(@p); 1; }; return $r if $ok; die(ref $@ ? $@->as_string() : $@); }
  0 0       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
366 84 50   84   167 sub registry { my ($self,@p)=@_; my @r; my $ok=eval { @r=$self->SUPER::registry(@p); 1; }; if ($ok) { return wantarray ? @r : $r[0]; } die(ref $@ ? $@->as_string() : $@); }
  84 50       132  
  84 0       117  
  84         370  
  84         148  
  84         252  
  84         370  
  0         0  
367 46 50   46   288 sub target { my ($self,@p)=@_; my $r; my $ok=eval { $r=$self->SUPER::target(@p); 1; }; return $r if $ok; die(ref $@ ? $@->as_string() : $@); }
  46 0       111  
  46         103  
  46         404  
  46         104  
  46         1149  
  0         0  
368 48 50   48   122 sub end { my ($self,@p)=@_; my $r; my $ok=eval { $r=$self->SUPER::end(@p); 1; }; return $r if $ok; die(ref $@ ? $@->as_string() : $@); }
  48 0       90  
  48         138  
  48         615  
  48         118  
  48         2217  
  0         0  
369              
370             sub AUTOLOAD
371             {
372 84     84   7819 my $self=shift;
373 84         134 my @r;
374 84         150 $Net::DRI::AUTOLOAD=$AUTOLOAD;
375 84         142 my $ok=eval { @r=$self->SUPER::AUTOLOAD(@_); 1; };
  84         461  
  39         74  
376 84 100       285 if (! $ok)
377             {
378 45         1406 my $err=$@;
379 45 50       342 die(ref $err ? $err->as_string() : $err);
380             }
381 39 50       202 return wantarray ? @r : $r[0];
382             }
383              
384             ####################################################################################################
385             1;