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__ |