File Coverage

blib/lib/Net/EPP/Client.pm
Criterion Covered Total %
statement 30 105 28.5
branch 0 54 0.0
condition 0 24 0.0
subroutine 9 19 47.3
pod 0 8 0.0
total 39 210 18.5


line stmt bran cond sub pod time code
1             # Copyright (c) 2016 CentralNic Ltd. All rights reserved. This program is
2             # free software; you can redistribute it and/or modify it under the same
3             # terms as Perl itself.
4             #
5             # $Id: Client.pm,v 1.17 2011/01/23 12:23:16 gavin Exp $
6             package Net::EPP::Client;
7 1     1   498 use bytes;
  1         8  
  1         4  
8 1     1   330 use Net::EPP::Protocol;
  1         1  
  1         22  
9 1     1   3 use Carp;
  1         2  
  1         37  
10 1     1   411 use IO::Socket;
  1         16589  
  1         3  
11 1     1   1051 use IO::Socket::SSL;
  1         45148  
  1         9  
12 1     1   136 use vars qw($XMLDOM $EPPFRAME);
  1         1  
  1         40  
13 1     1   3 use strict;
  1         1  
  1         17  
14 1     1   9 use warnings;
  1         1  
  1         82  
15              
16             =pod
17              
18             =head1 NAME
19              
20             Net::EPP::Client - a client library for the TCP transport for EPP, the Extensible Provisioning Protocol
21              
22             =head1 SYNOPSIS
23              
24             #!/usr/bin/perl
25             use Net::EPP::Client;
26             use strict;
27              
28             my $epp = Net::EPP::Client->new(
29             host => 'epp.nic.tld',
30             port => 700,
31             ssl => 1,
32             frames => 1,
33             );
34              
35             my $greeting = $epp->connect;
36              
37             $epp->send_frame('login.xml');
38              
39             my $answer = $epp->get_frame;
40              
41             $epp->send_frame('');
42              
43             my $answer = $epp->get_frame;
44              
45             =head1 DESCRIPTION
46              
47             EPP is the Extensible Provisioning Protocol. EPP (defined in RFC 4930) is an
48             application layer client-server protocol for the provisioning and management of
49             objects stored in a shared central repository. Specified in XML, the protocol
50             defines generic object management operations and an extensible framework that
51             maps protocol operations to objects. As of writing, its only well-developed
52             application is the provisioning of Internet domain names, hosts, and related
53             contact details.
54              
55             RFC 4934 defines a TCP based transport model for EPP, and this module
56             implements a client for that model. You can establish and manage EPP
57             connections and send and receive responses over this connection.
58              
59             C also provides some time-saving features, such as being able
60             to provide request and response frames as C objects.
61              
62             =cut
63              
64             BEGIN {
65 1     1   2 our $XMLDOM = 0;
66 1         1 our $EPPFRAME = 0;
67 1         47 eval {
68 1         182 require XML::LibXML;
69 0         0 $XMLDOM = 1;
70             };
71 1         2 eval {
72 1         354 require Net::EPP::Frame;
73 0           $EPPFRAME = 1;
74             };
75             }
76              
77             =pod
78              
79             =head1 CONSTRUCTOR
80              
81             my $epp = Net::EPP::Client->new(PARAMS);
82              
83             The constructor method creates a new EPP client object. It accepts a number of
84             parameters:
85              
86             =over
87              
88             =item * host
89              
90             C specifies the computer to connect to. This may be a DNS hostname or
91             an IP address.
92              
93             =item * port
94              
95             C specifies the TCP port to connect to. This is usually 700.
96              
97             =item * ssl
98              
99             If the C parameter is defined, then C will be used to
100             provide an encrypted connection. If not, then a plaintext connection will be
101             created.
102              
103             =item * dom (deprecated)
104              
105             If the C parameter is defined, then all response frames will be returned
106             as C objects.
107              
108             =item * frames
109              
110             If the C parameter is defined, then all response frames will be
111             returned as C objects (actually, C
112             objects reblessed as C objects).
113              
114             =back
115              
116             =cut
117              
118             sub new {
119 0     0 0   my ($package, %params) = @_;
120              
121 0           my $self;
122 0 0         if (defined($params{'sock'})) {
123             $self = {
124             'sock' => $params{'sock'},
125             ssl => 0,
126             'dom' => (defined($params{'dom'}) ? 1 : 0),
127 0 0         'frames' => (defined($params{'frames'}) ? 1 : 0),
    0          
128             }
129             } else {
130 0 0         croak("missing hostname") if (!defined($params{'host'}));
131 0 0         croak("missing port") if (!defined($params{'port'}));
132              
133             $self = {
134             'host' => $params{'host'},
135             'port' => $params{'port'},
136             'ssl' => (defined($params{'ssl'}) ? 1 : 0),
137             'dom' => (defined($params{'dom'}) ? 1 : 0),
138 0 0         'frames' => (defined($params{'frames'}) ? 1 : 0),
    0          
    0          
139             };
140             }
141              
142 0 0         if ($self->{'frames'} == 1) {
    0          
143 0 0         if ($EPPFRAME == 0) {
144 0           croak("Frames requested but Net::EPP::Frame isn't available");
145              
146             } else {
147 0           $self->{'class'} = 'Net::EPP::Frame';
148              
149             }
150              
151             } elsif ($self->{'dom'} == 1) {
152 0 0         if ($XMLDOM == 0) {
153 0           croak("DOM requested but XML::LibXML isn't available");
154              
155             } else {
156 0           $self->{'class'} = 'XML::LibXML::Document';
157              
158             }
159              
160             }
161              
162 0           return bless($self, $package);
163             }
164              
165             =pod
166              
167             =head1 METHODS
168              
169             =head2 Connecting to a server:
170              
171             my $greeting = $epp->connect(%PARAMS);
172              
173             This method establishes the TCP connection. You can use the C<%PARAMS> hash to
174             specify arguments that will be passed on to the constructors for
175             C (such as a timeout) or C (such as
176             certificate information). See the relevant manpage for examples.
177              
178             This method will C if connection fails, so be sure to use C if
179             you want to catch the error.
180              
181             By default, the return value for C will be the EPP EgreetingE
182             frame returned by the server. Please note that the same caveat about blocking
183             applies to this method as to C (see below).
184              
185             If you want to get the greeting yourself, set C<$params{no_greeting}>.
186              
187             =cut
188              
189             sub connect {
190 0     0 0   my ($self, %params) = @_;
191              
192 0 0         if (defined($self->{'sock'})) {
193 0           $self->_connect_unix(%params);
194              
195             } else {
196 0           $self->_connect_tcp(%params);
197              
198             }
199              
200 0 0         return ($params{'no_greeting'} ? 1 : $self->get_frame);
201              
202             }
203              
204             sub _connect_tcp {
205 0     0     my ($self, %params) = @_;
206              
207 0 0         my $SocketClass = ($self->{'ssl'} == 1 ? 'IO::Socket::SSL' : 'IO::Socket::INET');
208              
209             $self->{'connection'} = $SocketClass->new(
210             PeerAddr => $self->{'host'},
211 0           PeerPort => $self->{'port'},
212             Proto => 'tcp',
213             Type => SOCK_STREAM,
214             %params
215             );
216              
217 0 0 0       if (!defined($self->{'connection'}) || ($@ && $@ ne '')) {
      0        
218 0           chomp($@);
219 0           $@ =~ s/^$SocketClass:? ?//;
220 0           croak("Connection to $self->{'host'}:$self->{'port'} failed: $@")
221             };
222              
223 0           return 1;
224             }
225              
226             sub _connect_unix {
227 0     0     my ($self, %params) = @_;
228              
229             $self->{'connection'} = IO::Socket::UNIX->new(
230 0           Peer => $self->{'sock'},
231             Type => SOCK_STREAM,
232             %params
233             );
234              
235 0 0 0       croak("Connection to $self->{'host'}:$self->{'port'} failed: $@") if (!defined($self->{'connection'}) || ($@ && $@ ne ''));
      0        
236              
237 0           return 1;
238              
239             }
240              
241             =pod
242              
243             =head2 Communicating with the server:
244              
245             my $answer = $epp->request($question);
246              
247             This is a simple wrapper around C and C (see below).
248             This method accepts a "question" frame as an argument, sends it to the server,
249             and then returns the next frame the server sends back.
250              
251             =cut
252              
253             sub request {
254 0     0 0   my ($self, $frame) = @_;
255 0 0         return $self->get_frame if ($self->send_frame($frame));
256             }
257              
258             =pod
259              
260             =head2 Getting a frame from the server:
261              
262             my $frame = $epp->get_frame;
263              
264             This method returns an EPP response frame from the server. This may either be a
265             scalar filled with XML, an C object (or an
266             C object), depending on whether you defined the C
267             parameter to the constructor.
268              
269             B: this method will block your program until it receives the
270             full frame from the server. That could be a bad thing for your program, so you
271             might want to consider using the C function to apply a timeout, like
272             so:
273              
274             my $timeout = 10; # ten seconds
275              
276             eval {
277             local $SIG{ALRM} = sub { die "alarm\n" };
278             alarm($timeout);
279             my $frame = $epp->get_frame;
280             alarm(0);
281             };
282              
283             if ($@ ne '') {
284             alarm(0);
285             print "timed out\n";
286             }
287              
288             If the connection to the server closes before the response can be received, or
289             the server returned a mal-formed frame, this method will C.
290              
291             =cut
292              
293             sub get_frame {
294 0     0 0   my $self = shift;
295 0           return $self->get_return_value(Net::EPP::Protocol->get_frame($self->{'connection'}));
296             }
297              
298             sub get_return_value {
299 0     0 0   my ($self, $xml) = @_;
300              
301 0 0         if (!defined($self->{'class'})) {
302 0           return $xml;
303              
304             } else {
305 0           my $document;
306 0           eval { $document = $self->parser->parse_string($xml) };
  0            
307 0 0 0       if (!defined($document) || $@ ne '') {
308 0           chomp($@);
309 0           croak(sprintf("Frame from server wasn't well formed: %s\n\nThe XML looks like this:\n\n%s\n\n", $@, $xml));
310 0           return undef;
311              
312             } else {
313 0           my $class = $self->{'class'};
314 0           return bless($document, $class);
315              
316             }
317             }
318             }
319              
320             =pod
321              
322             =head2 Sending a frame to the server:
323              
324             $epp->send_frame($frame, $wfcheck);
325              
326             This sends a request frame to the server. C<$frame> may be one of:
327              
328             =over
329              
330             =item * a scalar containing XML
331              
332             =item * a scalar containing a filename
333              
334             =item * an C object (or an instance of a subclass)
335              
336             =item * an C object (or an instance of a subclass)
337              
338             =back
339              
340             Unless C<$wfcheck> is false, the first two of these will be checked for
341             well-formedness. If the XML data is broken, then this method will croak.
342              
343             =cut
344              
345             sub send_frame {
346 0     0 0   my ($self, $frame, $wfcheck) = @_;
347              
348 0           my $xml;
349 0 0 0       if (ref($frame) ne '' && ($frame->isa('XML::DOM::Document') || $frame->isa('XML::LibXML::Document'))) {
    0 0        
      0        
350 0           $xml = $frame->toString;
351 0           $wfcheck = 0;
352              
353             } elsif ($frame !~ /
354 0 0         if (!open(FRAME, $frame)) {
355 0           croak("Couldn't open file '$frame' for reading: $!");
356              
357             } else {
358 0           $xml = join('', );
359 0           close(FRAME);
360 0           $wfcheck = 1;
361              
362             }
363              
364             } else {
365 0           $xml = $frame;
366 0 0         $wfcheck = ($wfcheck ? 1 : 0);
367              
368             }
369              
370 0 0         if ($wfcheck == 1) {
371 0           eval { $self->parser->parse_string($xml) };
  0            
372 0 0         if ($@ ne '') {
373 0           chomp($@);
374 0           croak(sprintf("Frame from server wasn't well formed: %s\n\nThe XML looks like this:\n\n%s\n\n", $@, $xml));
375             }
376             }
377              
378 0           return Net::EPP::Protocol->send_frame($self->{'connection'}, $xml);
379             }
380              
381             =pod
382              
383             =head2 Disconnecting from the server:
384              
385             $epp->disconnect;
386              
387             This closes the connection. An EPP server should always close a connection after
388             a ElogoutE frame has been received and acknowledged; this method
389             is provided to allow you to clean up on the client side, or close the
390             connection out of sync with the server.
391              
392             =cut
393              
394             sub disconnect {
395 0     0 0   my $self = shift;
396 0           $self->{'connection'}->close;
397 0           return 1;
398             }
399              
400             =pod
401              
402             =head1 AUTHOR
403              
404             CentralNic Ltd (L).
405              
406             =head1 COPYRIGHT
407              
408             This module is (c) 2016 CentralNic Ltd. This module is free software; you can
409             redistribute it and/or modify it under the same terms as Perl itself.
410              
411             =head1 SEE ALSO
412              
413             =over
414              
415             =item * L
416              
417             =item * L
418              
419             =item * RFCs 4930 and RFC 4934, available from L.
420              
421             =item * The CentralNic EPP site at L.
422              
423             =back
424              
425             =cut
426              
427             sub parser {
428 0     0 0   my $self = shift;
429 0 0         $self->{'parser'} = XML::LibXML->new if (!$self->{'parser'});
430 0           return $self->{'parser'};
431             }
432              
433             1;