| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package UltraDNS; |
|
2
|
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
169629
|
use warnings; |
|
|
6
|
|
|
|
|
15
|
|
|
|
6
|
|
|
|
|
182
|
|
|
4
|
6
|
|
|
6
|
|
32
|
use strict; |
|
|
6
|
|
|
|
|
10
|
|
|
|
6
|
|
|
|
|
176
|
|
|
5
|
6
|
|
|
6
|
|
37
|
use Carp; |
|
|
6
|
|
|
|
|
16
|
|
|
|
6
|
|
|
|
|
793
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
UltraDNS - Client API for the NeuStar UltraDNS Transaction Protocol |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use UltraDNS; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# establish a secure connection |
|
18
|
|
|
|
|
|
|
my $udns = UltraDNS->connect("$host:$port", $sponsor, $username, $password); |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Queue up one or more actions to be performed |
|
21
|
|
|
|
|
|
|
$udns->CreateARecord($zone); |
|
22
|
|
|
|
|
|
|
$udns->CreateCNAMERecord($zone); |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Send actions as a single transaction |
|
25
|
|
|
|
|
|
|
$udns->commit(...); # throws exception on error |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# queue up and commit more requests on the same connection |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Getting multiple results: |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# Actions can return results. Each return value is a reference |
|
32
|
|
|
|
|
|
|
# to where the result will be stored when commit() is called. |
|
33
|
|
|
|
|
|
|
$result_ref1 = $udns->GetZoneInfo($zone); |
|
34
|
|
|
|
|
|
|
$result_ref2 = $udns->GetMXRecordsOfZone($zone); |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
$udns->commit(...); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# $result_ref values above now refer to the RPC::XML results for |
|
39
|
|
|
|
|
|
|
# each method, use ($$result_ref1)->value to get the value |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Getting a single result: |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# utility method that calls commit and returns the dereferenced result |
|
44
|
|
|
|
|
|
|
$result = $udns->do( ...some method that queues a request... ); |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
$result = $udns->do( $udns->AutoSerialUpdateState ); |
|
47
|
|
|
|
|
|
|
# $result is either 1 or 0 (no need to deref or call value() method) |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# also works for multiple method calls |
|
50
|
|
|
|
|
|
|
@results = $udns->do( ...multiple method calls... ); |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
A simple and efficient client for the NeuStar UltraDNS Transaction Protocol as |
|
55
|
|
|
|
|
|
|
defined in L (version 3.0, dated |
|
56
|
|
|
|
|
|
|
September 5, 2008). |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
All requests are batched and performed in transactions. A single secure |
|
59
|
|
|
|
|
|
|
connection is established and reused for any number of transactions. |
|
60
|
|
|
|
|
|
|
Multiple concurrent connections can be used if required. |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
All errors are reported via exceptions. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 STATUS |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
All UltraDNS methods are supported. |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Experimentation and feedback are encouraged. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=head1 METHODS |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
|
73
|
|
|
|
|
|
|
|
|
74
|
6
|
|
|
6
|
|
5413
|
use Symbol qw(gensym); |
|
|
6
|
|
|
|
|
13032
|
|
|
|
6
|
|
|
|
|
424
|
|
|
75
|
6
|
|
|
6
|
|
6707
|
use Data::Dumper; |
|
|
6
|
|
|
|
|
65695
|
|
|
|
6
|
|
|
|
|
471
|
|
|
76
|
6
|
|
|
6
|
|
8561
|
use IO::Socket::INET; |
|
|
6
|
|
|
|
|
182429
|
|
|
|
6
|
|
|
|
|
50
|
|
|
77
|
6
|
|
|
6
|
|
12192
|
use Net::SSLeay qw(die_now die_if_ssl_error); |
|
|
6
|
|
|
|
|
76182
|
|
|
|
6
|
|
|
|
|
4051
|
|
|
78
|
|
|
|
|
|
|
|
|
79
|
6
|
|
|
6
|
|
4284
|
use UltraDNS::Parser; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
use UltraDNS::Type; |
|
81
|
|
|
|
|
|
|
use UltraDNS::Methods; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# initialise Net::SSLeay |
|
85
|
|
|
|
|
|
|
Net::SSLeay::load_error_strings(); |
|
86
|
|
|
|
|
|
|
Net::SSLeay::SSLeay_add_ssl_algorithms(); |
|
87
|
|
|
|
|
|
|
Net::SSLeay::randomize(); |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head2 connect |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
$udns = UltraDNS->connect($host_and_port, $sponsor, $username, $password, $attr); |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Establish a secure C connection to the specified UltraDNS host and port, |
|
95
|
|
|
|
|
|
|
and login using the specified $sponsor, $username, $password. |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Returns an C object. Throws an exception on error. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
The optional $attr parameter is a reference to a hash of attributes: |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=over 4 |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item trace |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Specifies the integer trace (debug) level. 0 for none, 1 for basic tracing, and |
|
106
|
|
|
|
|
|
|
2 and above for more detailed, and more verbose, tracing. Trace messages are |
|
107
|
|
|
|
|
|
|
output via C. |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=item ssl_trace |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Sets $Net::SSLeay::trace. 0=no warns, 1=only errors, 2=ciphers, 3=progress, 4=dump data. |
|
112
|
|
|
|
|
|
|
See L for more information. |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=item version |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Specifies the protocol version argument value used in the C request. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=back |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
See L for a list of the UltraDNS Transaction Protocol |
|
121
|
|
|
|
|
|
|
methods you can call once a connection is established. |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub connect { ## no critic (ProhibitBuiltinHomonyms) |
|
126
|
|
|
|
|
|
|
my ($class, $host_and_port, $sponsor, $username, $password, $attr) = @_; |
|
127
|
|
|
|
|
|
|
$host_and_port ||= "api.ultradns.net:8755"; |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my $version = $attr->{version} || '3.0'; |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# create an underlying raw socket and connect it to the UltraDNS server |
|
132
|
|
|
|
|
|
|
my $socket = IO::Socket::INET->new(PeerAddr => $host_and_port) |
|
133
|
|
|
|
|
|
|
or croak ("Error connecting to $host_and_port: $!"); |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# create a new SSL instance and link it to the socket |
|
136
|
|
|
|
|
|
|
$Net::SSLeay::trace = $attr->{ssl_trace} if $attr->{ssl_trace}; |
|
137
|
|
|
|
|
|
|
my $ctx = Net::SSLeay::CTX_new() or die_now("Failed to create SSL_CTX $!"); |
|
138
|
|
|
|
|
|
|
my $ssl = Net::SSLeay::new($ctx) or die_now("Failed to create SSL $!"); |
|
139
|
|
|
|
|
|
|
Net::SSLeay::set_fd($ssl, fileno($socket)); # Must use fileno |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# connect and negotiate at the SSL level |
|
142
|
|
|
|
|
|
|
my $resp = Net::SSLeay::connect($ssl); |
|
143
|
|
|
|
|
|
|
die_if_ssl_error("SSL connect failed"); |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
my $self = bless { |
|
146
|
|
|
|
|
|
|
peer_addr => $host_and_port, |
|
147
|
|
|
|
|
|
|
user => $username, |
|
148
|
|
|
|
|
|
|
fh => $socket, # just to hold ref to underlying socket |
|
149
|
|
|
|
|
|
|
ssl => $ssl, |
|
150
|
|
|
|
|
|
|
queue => [], |
|
151
|
|
|
|
|
|
|
err => 0, |
|
152
|
|
|
|
|
|
|
errstr=> '', |
|
153
|
|
|
|
|
|
|
} => $class; |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
$self->trace($attr->{trace} || $ENV{ULTRADNS_TRACE}); |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$self->_send_xml( join "", |
|
158
|
|
|
|
|
|
|
"", |
|
159
|
|
|
|
|
|
|
"", |
|
160
|
|
|
|
|
|
|
); # no reply at this point |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
$self->OpenConnection($sponsor, $username, $password, $version); |
|
163
|
|
|
|
|
|
|
$self->NoAutoCommit(); # for transaction safety |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $commit = $self->commit; |
|
166
|
|
|
|
|
|
|
# sanity check - probably not needed as commit() throws an exception |
|
167
|
|
|
|
|
|
|
# if the server returns a fault response |
|
168
|
|
|
|
|
|
|
$self->_throw_error("Initial setup failed: $$commit") |
|
169
|
|
|
|
|
|
|
unless $$commit eq 'Transaction succeeded'; |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
return $self; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub DESTROY { |
|
175
|
|
|
|
|
|
|
# nothing extra needed, just let perl look after it |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 commit |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
$udns->commit; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Submits the queued requests. An exception is thown on error. |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=cut |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub commit { |
|
188
|
|
|
|
|
|
|
my ($self) = @_; |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
my $queue = $self->{queue}; |
|
191
|
|
|
|
|
|
|
# reset the object state |
|
192
|
|
|
|
|
|
|
$self->{queue} = []; |
|
193
|
|
|
|
|
|
|
$self->{err} = 0; |
|
194
|
|
|
|
|
|
|
$self->{errstr} = ''; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$self->_trace(sprintf "committing %d requests (+1 transaction)\n", scalar @$queue); |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $xml = join "\n", map { $_->{xml} } @$queue; |
|
199
|
|
|
|
|
|
|
$self->_send_xml( "\n$xml\n" ); |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
$self->{stats}{transactions}++; |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my ($responses, $response_xml) = $self->_get_responses; |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Shift the @$responses into the result slots in @$queue. |
|
206
|
|
|
|
|
|
|
# We expect one more item in @$responses than @$queue because @$responses |
|
207
|
|
|
|
|
|
|
# should have an extra 'Transaction succeeded' at the end. |
|
208
|
|
|
|
|
|
|
while (@$queue) { |
|
209
|
|
|
|
|
|
|
croak "Didn't get responses for all methods in transaction" |
|
210
|
|
|
|
|
|
|
if @$responses == 0; |
|
211
|
|
|
|
|
|
|
my $slot = shift @$queue; |
|
212
|
|
|
|
|
|
|
my $response = shift @$responses; |
|
213
|
|
|
|
|
|
|
my $xml = shift @$response_xml; |
|
214
|
|
|
|
|
|
|
$self->_throw_error("Unexpected response: $response ($xml)") |
|
215
|
|
|
|
|
|
|
if not UNIVERSAL::can($response, 'is_fault'); |
|
216
|
|
|
|
|
|
|
$self->_throw_fault($response->value, $slot->{shortmess}) |
|
217
|
|
|
|
|
|
|
if $response->is_fault; |
|
218
|
|
|
|
|
|
|
$slot->{result} = $response->value; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
my $response = shift @$responses; |
|
221
|
|
|
|
|
|
|
$self->_throw_fault($response->value, "commit") |
|
222
|
|
|
|
|
|
|
if $response->is_fault; |
|
223
|
|
|
|
|
|
|
$self->_throw_error("Unexpected extra responses after commit") |
|
224
|
|
|
|
|
|
|
if @$responses; |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
return $response->value; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub _throw_fault { |
|
231
|
|
|
|
|
|
|
my ($self, $fault, $what) = @_; |
|
232
|
|
|
|
|
|
|
# record the error details in the object |
|
233
|
|
|
|
|
|
|
my $err = $self->{err} = $fault->code; |
|
234
|
|
|
|
|
|
|
my $errstr = $self->{errstr} = $fault->string; |
|
235
|
|
|
|
|
|
|
my $msg = "$what failed with server-side error $err: $errstr"; |
|
236
|
|
|
|
|
|
|
return $self->_throw_error($msg); # doesn't return |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 rollback |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
$udns->rollback; |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Discards the queued requests. |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=cut |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub rollback { |
|
249
|
|
|
|
|
|
|
shift->{queue} = []; |
|
250
|
|
|
|
|
|
|
return; |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head2 do |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
$result = $udns->do( $udns->SomeMethodThatReturnsAResult(...) ); |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
A convienience method that calls commit() and returns the de-referenced |
|
259
|
|
|
|
|
|
|
argument. The one-line call has the same effect as these three lines: |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
$result_ref = $udns->SomeMethodThatReturnsAResult(...); |
|
262
|
|
|
|
|
|
|
$udns->commit; |
|
263
|
|
|
|
|
|
|
$result = $$result_ref; # de-reference to get return value |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
but is much more convienient when you just want to get a value from the server. |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Multiple calls can be combined into a single request like this: |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
my ($a, $b, $c) = $udns->do( |
|
270
|
|
|
|
|
|
|
$udns->MethodReturningA(...), |
|
271
|
|
|
|
|
|
|
$udns->MethodReturningB(...), |
|
272
|
|
|
|
|
|
|
$udns->MethodReturningC(...) |
|
273
|
|
|
|
|
|
|
); |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=cut |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub do { |
|
278
|
|
|
|
|
|
|
my ($self, @result_refs) = @_; |
|
279
|
|
|
|
|
|
|
croak "Can't call do() without an UltraDNS object reference" |
|
280
|
|
|
|
|
|
|
unless ref $self and UNIVERSAL::isa($self, __PACKAGE__); |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
my $queue = $self->{queue}; |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
$self->_throw_error(sprintf "do() called with %d arguments but %d actions are queued (%s)", |
|
285
|
|
|
|
|
|
|
scalar @result_refs, scalar @$queue, |
|
286
|
|
|
|
|
|
|
join(", ", map { "$_->{method} at $_->{shortmess}" } @$queue) ) |
|
287
|
|
|
|
|
|
|
if scalar @result_refs != scalar @$queue; |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
$self->_throw_error("do() called in scalar context but with more than one argument") |
|
290
|
|
|
|
|
|
|
if not wantarray and @result_refs > 1; |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# we're asked to do nothing, so we return nothing |
|
293
|
|
|
|
|
|
|
return unless @result_refs; |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
$self->commit; |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
my @results = map { ($$_)->value } @result_refs; |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
return $results[0] if not wantarray; |
|
300
|
|
|
|
|
|
|
return @results; |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head2 eval |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Just like the L method except any exception will be caught. |
|
307
|
|
|
|
|
|
|
This is useful for cases where an error is expected, such as deleting a record |
|
308
|
|
|
|
|
|
|
in the server that may not exist. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
XXX currently it catches all exceptions, it's expected that in future it will |
|
311
|
|
|
|
|
|
|
only catch exceptions due to server-reported error. |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=cut |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub eval { ## no critic (ProhibitBuiltinHomonyms) |
|
316
|
|
|
|
|
|
|
my @results = eval { shift->do( @_ ) }; |
|
317
|
|
|
|
|
|
|
return @results; # empty if do() threw an exception |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=head2 err |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
$err = $udns->err; |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Returns the error code from the server for the last transaction, else 0. |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 errstr |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
$errstr = $udns->errstr; |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Returns the error message from the server for the last transaction, else an empty string. |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=cut |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub err { return shift->{err} } |
|
336
|
|
|
|
|
|
|
sub errstr { return shift->{errstr} } |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=head2 trace |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
$udns->trace($level); |
|
342
|
|
|
|
|
|
|
$prev = $udns->trace($level); |
|
343
|
|
|
|
|
|
|
$prev = $udns->trace; |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Sets the new trace level, if a value is supplied. |
|
346
|
|
|
|
|
|
|
0 = off, 1 = basic overview, 2+ = more details. |
|
347
|
|
|
|
|
|
|
Returns the previous level. |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=cut |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub trace { ## no critic (RequireArgUnpacking) |
|
352
|
|
|
|
|
|
|
my $self = shift; |
|
353
|
|
|
|
|
|
|
my $prev = $self->{trace} || 0; |
|
354
|
|
|
|
|
|
|
$self->{trace} = shift || 0 if @_; |
|
355
|
|
|
|
|
|
|
$self->_trace("trace level set to $self->{trace}") if $self->{trace} or $prev; |
|
356
|
|
|
|
|
|
|
return $prev; |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# --- |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub _description { |
|
364
|
|
|
|
|
|
|
my $self = shift; |
|
365
|
|
|
|
|
|
|
return sprintf "UltraDNS %s@%s", $self->{user}, $self->{peer_addr}; |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# _throw_error message shouldn't have newline at end |
|
370
|
|
|
|
|
|
|
sub _throw_error { |
|
371
|
|
|
|
|
|
|
my $self = shift; |
|
372
|
|
|
|
|
|
|
$self->_trace("error: @_\n"); |
|
373
|
|
|
|
|
|
|
croak $self->_description . " error: @_"; |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
sub _warn { carp shift->_description . ": @_\n"; return } |
|
376
|
|
|
|
|
|
|
# _trace message argument should have newline at end |
|
377
|
|
|
|
|
|
|
sub _trace { warn "UltraDNS: @_" if shift->{trace}; return } |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub _send_xml { |
|
381
|
|
|
|
|
|
|
my ($self, $xml) = @_; |
|
382
|
|
|
|
|
|
|
$self->_trace("_send_xml $xml") if $self->{trace} >= 2; |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Net::SSLeay::write($self->{ssl}, "$xml\r\n") |
|
385
|
|
|
|
|
|
|
or $self->_throw_error("sending request: $!"); |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
return; |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub _get_xml { |
|
392
|
|
|
|
|
|
|
my $self = shift; |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
$self->_trace("_get_xml awaiting response") if $self->{trace} >= 2; |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# if we always use a transaction then we can use (note the plural) |
|
397
|
|
|
|
|
|
|
# to identify the end of the server response to our request, saving ourselves |
|
398
|
|
|
|
|
|
|
# a whole bunch of headaches and inefficiencies |
|
399
|
|
|
|
|
|
|
my $response_body = Net::SSLeay::ssl_read_until($self->{ssl}, ""); |
|
400
|
|
|
|
|
|
|
$self->_trace(Dumper($response_body)) if $self->{trace} >= 2; |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
return $response_body; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub _get_responses { |
|
407
|
|
|
|
|
|
|
my ($self) = @_; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
my $response_body = $self->_get_xml; |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# RPC::XML can't handle the UltraDNS methodResponses (plural) |
|
412
|
|
|
|
|
|
|
# so we chop out and process each individual methodResponse in turn |
|
413
|
|
|
|
|
|
|
my @response_xml; |
|
414
|
|
|
|
|
|
|
my @responses; |
|
415
|
|
|
|
|
|
|
while ($response_body =~ s{(.*?)}{}m) { |
|
416
|
|
|
|
|
|
|
my $xml = $1; |
|
417
|
|
|
|
|
|
|
push @response_xml, $xml; |
|
418
|
|
|
|
|
|
|
my $resp = UltraDNS::Parser->_parse_rpc_xml($xml); |
|
419
|
|
|
|
|
|
|
push @responses, $resp; |
|
420
|
|
|
|
|
|
|
print "XML: $xml:\nPerl: ".Dumper($resp) if $self->{trace} >= 3; |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
$self->_throw_error("No responses found in $response_body") |
|
424
|
|
|
|
|
|
|
unless @responses; |
|
425
|
|
|
|
|
|
|
$self->_trace("_get_responses received ".scalar(@responses)." responses") |
|
426
|
|
|
|
|
|
|
if $self->{trace} >= 2; |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# cleanse and sanity check the remaining rump of $response_body |
|
429
|
|
|
|
|
|
|
$response_body =~ s{<\?xml version=".*?"\?>}{}; |
|
430
|
|
|
|
|
|
|
$response_body =~ s{\s*\s*\s*}{}; |
|
431
|
|
|
|
|
|
|
if ($response_body ne '') { |
|
432
|
|
|
|
|
|
|
$self->_warn("Unprocessed remnants in response body: '$response_body'"); |
|
433
|
|
|
|
|
|
|
} |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
return \@responses unless wantarray; |
|
436
|
|
|
|
|
|
|
return (\@responses, \@response_xml); |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub AUTOLOAD { ## no critic (RequireArgUnpacking) |
|
441
|
|
|
|
|
|
|
(my $method = our $AUTOLOAD) =~ s/.*::(?:UDNS_)?//; |
|
442
|
|
|
|
|
|
|
my $self = shift; |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# sanity check to avoid obscure errors when users do odd things |
|
445
|
|
|
|
|
|
|
croak sprintf "Can't call %s->%s() because '%s' isn't an UltraDNS object reference", |
|
446
|
|
|
|
|
|
|
$self, $method, $self |
|
447
|
|
|
|
|
|
|
unless ref $self and UNIVERSAL::isa($self, __PACKAGE__); |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
return $self->_enqueue_method_call($method, \@_); |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub _shortmess { # much faster version of Carp::shortmess |
|
454
|
|
|
|
|
|
|
my ($self, $what) = @_; |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
my ($pkg, $file, $line, $level); |
|
457
|
|
|
|
|
|
|
do { ($pkg, $file, $line) = caller(++$level) } while $pkg =~ /^UltraDNS\b/; |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
my $shortmess = "$file line $line"; |
|
460
|
|
|
|
|
|
|
$shortmess = "$what at $shortmess" if $what; |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
return $shortmess; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub _enqueue_method_call { |
|
467
|
|
|
|
|
|
|
my ($self, $method, $args) = @_; |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my $shortmess = $self->_shortmess($method); |
|
470
|
|
|
|
|
|
|
$self->_trace($shortmess) |
|
471
|
|
|
|
|
|
|
if $self->{trace}; |
|
472
|
|
|
|
|
|
|
$self->{stats}{methods}{$method}++; |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
my $xml = $self->_xml_for_method_call($method, $args); |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
return $self->_enqueue_xml($xml, { |
|
477
|
|
|
|
|
|
|
method => $method, |
|
478
|
|
|
|
|
|
|
args => $args, |
|
479
|
|
|
|
|
|
|
shortmess => $shortmess, |
|
480
|
|
|
|
|
|
|
}); |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub _enqueue_xml { |
|
485
|
|
|
|
|
|
|
my ($self, $xml, $meta) = @_; |
|
486
|
|
|
|
|
|
|
my $queue = $self->{queue}; |
|
487
|
|
|
|
|
|
|
push @$queue, { %{ $meta || {} }, xml => $xml, result => undef }; |
|
488
|
|
|
|
|
|
|
# return reference to the slot that will hold the result value for this method call |
|
489
|
|
|
|
|
|
|
return \$queue->[-1]{result}; |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub _xml_for_method_call { |
|
494
|
|
|
|
|
|
|
my ($self, $method, $args) = @_; |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
my $method_info = UltraDNS::Methods->_method_spec("UDNS_$method") |
|
497
|
|
|
|
|
|
|
or croak "Can't call unknown method '$method'"; # XXX could allow later |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
my $arg_info = $method_info->{arg_info}; |
|
500
|
|
|
|
|
|
|
if (@$args < @$arg_info) { |
|
501
|
|
|
|
|
|
|
croak sprintf "%s called with too few parameters (has %d parameters but %d only arguments were given)", |
|
502
|
|
|
|
|
|
|
$method, scalar @$arg_info, scalar @$args; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
if (@$args > @$arg_info) { |
|
505
|
|
|
|
|
|
|
croak sprintf "%s called with too many parameters (has %d parameters but %d arguments were given)", |
|
506
|
|
|
|
|
|
|
$method, scalar @$arg_info, scalar @$args |
|
507
|
|
|
|
|
|
|
unless $method_info->{last_arg_repeats}; |
|
508
|
|
|
|
|
|
|
# clone and pad out arg_info with copies of the final arg |
|
509
|
|
|
|
|
|
|
$arg_info = [ @$arg_info ]; |
|
510
|
|
|
|
|
|
|
push @$arg_info, $arg_info->[-1] while @$arg_info < @$args; |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
my @value_xml; |
|
514
|
|
|
|
|
|
|
for my $arg_info (@$arg_info) { |
|
515
|
|
|
|
|
|
|
my $value = shift @$args; |
|
516
|
|
|
|
|
|
|
my $class = "RPC::XML::$arg_info->{type}"; |
|
517
|
|
|
|
|
|
|
my $value_obj = $class->new($value); |
|
518
|
|
|
|
|
|
|
push @value_xml, sprintf "%s\n", |
|
519
|
|
|
|
|
|
|
$value_obj->as_string; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
return "UDNS_$method\n@value_xml\n"; |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
1; |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
__END__ |