line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
## Domain Registry Interface, TCP/SSL Socket Transport |
2
|
|
|
|
|
|
|
## |
3
|
|
|
|
|
|
|
## Copyright (c) 2005-2013 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::Transport::Socket; |
16
|
|
|
|
|
|
|
|
17
|
1
|
|
|
1
|
|
859
|
use base qw(Net::DRI::Transport); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
76
|
|
18
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
21
|
|
20
|
1
|
|
|
1
|
|
9
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
21
|
|
21
|
|
|
|
|
|
|
|
22
|
1
|
|
|
1
|
|
5
|
use Time::HiRes (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
23
|
1
|
|
|
1
|
|
4
|
use IO::Socket::INET; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
14
|
|
24
|
|
|
|
|
|
|
## At least this version is needed, to have getline() |
25
|
1
|
|
|
1
|
|
1363
|
use IO::Socket::SSL 0.90; |
|
1
|
|
|
|
|
42283
|
|
|
1
|
|
|
|
|
9
|
|
26
|
|
|
|
|
|
|
|
27
|
1
|
|
|
1
|
|
222
|
use Net::DRI::Exception; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
21
|
|
28
|
1
|
|
|
1
|
|
4
|
use Net::DRI::Util; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
16
|
|
29
|
1
|
|
|
1
|
|
5
|
use Net::DRI::Data::Raw; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=pod |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 NAME |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
Net::DRI::Transport::Socket - TCP/TLS Socket connection for Net::DRI |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 DESCRIPTION |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
This module implements a socket (tcp or tls) for establishing connections in Net::DRI |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 METHODS |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
At creation (see Net::DRI C) you pass a reference to an hash, with the following available keys: |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head2 socktype |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
ssl, tcp or udp |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head2 ssl_key_file ssl_cert_file ssl_ca_file ssl_ca_path ssl_cipher_list ssl_version ssl_passwd_cb ssl_hostname |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
if C is 'ssl', all key materials, see IO::Socket::SSL documentation for corresponding options |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head2 ssl_verify |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
see IO::Socket::SSL documentation about verify_mode (by default 0x00 here) |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head2 ssl_verify_callback |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
see IO::Socket::SSL documentation about verify_callback, it gets here as first parameter the transport object |
61
|
|
|
|
|
|
|
then all parameter given by IO::Socket::SSL; it is explicitly verified that the subroutine returns a true value, |
62
|
|
|
|
|
|
|
and if not the connection is aborted. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 remote_host remote_port |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
hostname (or IP address) & port number of endpoint |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 client_login client_password |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
protocol login & password |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head2 client_newpassword |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
(optional) new password if you want to change password on login for registries handling that at connection |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head2 protocol_connection |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Net::DRI class handling protocol connection details. (Ex: C or C) |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head2 protocol_data |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
(optional) opaque data given to protocol_connection class. |
83
|
|
|
|
|
|
|
For EPP, a key login_service_filter may exist, whose value is a code ref. It will be given an array of services, and should give back a |
84
|
|
|
|
|
|
|
similar array; it can be used to filter out some services from those given by the registry. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head2 close_after |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
number of protocol commands to send to server (we will automatically close and re-open connection if needed) |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head2 local_host |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
(optional) the local address (hostname or IP) you want to use to connect |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head1 SUPPORT |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
For now, support questions should be sent to: |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Enetdri@dotandco.comE |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Please also see the SUPPORT file in the distribution. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head1 SEE ALSO |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
http://www.dotandco.com/services/software/Net-DRI/ |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head1 AUTHOR |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Patrick Mevzek, Enetdri@dotandco.comE |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head1 COPYRIGHT |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Copyright (c) 2005-2013 Patrick Mevzek . |
113
|
|
|
|
|
|
|
All rights reserved. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
116
|
|
|
|
|
|
|
it under the terms of the GNU General Public License as published by |
117
|
|
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or |
118
|
|
|
|
|
|
|
(at your option) any later version. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
See the LICENSE file that comes with this distribution for more details. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=cut |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
#################################################################################################### |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub new |
127
|
|
|
|
|
|
|
{ |
128
|
0
|
|
|
0
|
1
|
|
my ($class,$ctx,$rp)=@_; |
129
|
0
|
|
|
|
|
|
my %opts=%$rp; |
130
|
0
|
|
|
|
|
|
my $po=$ctx->{protocol}; |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
my %t=(message_factory => $po->factories()->{message}); |
133
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('protocol_connection') unless (exists($opts{protocol_connection}) && $opts{protocol_connection}); |
134
|
0
|
|
|
|
|
|
$t{pc}=$opts{protocol_connection}; |
135
|
0
|
|
|
|
|
|
Net::DRI::Util::load_module($t{pc},'transport/socket'); |
136
|
0
|
0
|
|
|
|
|
if ($t{pc}->can('transport_default')) |
137
|
|
|
|
|
|
|
{ |
138
|
0
|
|
|
|
|
|
%opts=($t{pc}->transport_default('socket_inet'),%opts); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
my $self=$class->SUPER::new($ctx,\%opts); ## We are now officially a Net::DRI::Transport instance |
142
|
0
|
0
|
|
|
|
|
$self->has_state(exists $opts{has_state}? $opts{has_state} : 1); |
143
|
0
|
|
|
|
|
|
$self->is_sync(1); |
144
|
0
|
|
|
|
|
|
$self->name('socket_inet'); |
145
|
0
|
|
|
|
|
|
$self->version('0.8'); |
146
|
|
|
|
|
|
|
##delete($ctx->{protocol}); ## TODO : double check it is ok |
147
|
0
|
|
|
|
|
|
delete($ctx->{registry}); |
148
|
0
|
|
|
|
|
|
delete($ctx->{profile}); |
149
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('socktype must be defined') unless (exists($opts{socktype})); |
151
|
0
|
0
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('socktype must be ssl, tcp or udp') unless ($opts{socktype}=~m/^(ssl|tcp|udp)$/); |
152
|
0
|
|
|
|
|
|
$t{socktype}=$opts{socktype}; |
153
|
0
|
|
|
|
|
|
$t{client_login}=$opts{client_login}; |
154
|
0
|
|
|
|
|
|
$t{client_password}=$opts{client_password}; |
155
|
0
|
0
|
0
|
|
|
|
$t{client_newpassword}=$opts{client_newpassword} if (exists($opts{client_newpassword}) && $opts{client_newpassword}); |
156
|
|
|
|
|
|
|
|
157
|
0
|
0
|
0
|
|
|
|
$t{protocol_data}=$opts{protocol_data} if (exists($opts{protocol_data}) && $opts{protocol_data}); |
158
|
0
|
|
|
|
|
|
my @need=qw/read_data write_message/; |
159
|
0
|
0
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('protocol_connection class ('.$t{pc}.') must have: '.join(' ',@need)) if (grep { ! $t{pc}->can($_) } @need); |
|
0
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
|
161
|
0
|
0
|
0
|
|
|
|
if (exists($opts{find_remote_server}) && defined($opts{find_remote_server}) && $t{pc}->can('find_remote_server')) |
|
|
|
0
|
|
|
|
|
162
|
|
|
|
|
|
|
{ |
163
|
0
|
|
|
|
|
|
($opts{remote_host},$opts{remote_port})=$t{pc}->find_remote_server($self,$opts{find_remote_server}); |
164
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{phase=>'opening',message=>'Found the following remote_host:remote_port = '.$opts{remote_host}.':'.$opts{remote_port}}); |
165
|
|
|
|
|
|
|
} |
166
|
0
|
|
|
|
|
|
foreach my $p ('remote_host','remote_port','protocol_version') |
167
|
|
|
|
|
|
|
{ |
168
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters($p.' must be defined') unless (exists($opts{$p}) && $opts{$p}); |
169
|
0
|
|
|
|
|
|
$t{$p}=$opts{$p}; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('close_after must be an integer') if ($opts{close_after} && !Net::DRI::Util::isint($opts{close_after})); |
173
|
0
|
|
0
|
|
|
|
$t{close_after}=$opts{close_after} || 0; |
174
|
|
|
|
|
|
|
|
175
|
0
|
0
|
|
|
|
|
if ($t{socktype} eq 'ssl') |
176
|
|
|
|
|
|
|
{ |
177
|
0
|
|
|
|
|
|
$t{ssl_context}=$self->parse_ssl_options(\%opts); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
0
|
0
|
0
|
|
|
|
$t{local_host}=$opts{local_host} if (exists($opts{local_host}) && $opts{local_host}); |
181
|
0
|
|
|
|
|
|
$t{remote_uri}=sprintf('%s://%s:%d',$t{socktype},$t{remote_host},$t{remote_port}); ## handy shortcut only used for error messages |
182
|
0
|
|
|
|
|
|
$self->{transport}=\%t; |
183
|
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
my $rc; |
185
|
0
|
0
|
|
|
|
|
if ($self->defer()) ## we will open, but later |
186
|
|
|
|
|
|
|
{ |
187
|
0
|
|
|
|
|
|
$self->current_state(0); |
188
|
|
|
|
|
|
|
} else ## we will open NOW |
189
|
|
|
|
|
|
|
{ |
190
|
0
|
|
|
|
|
|
$rc=$self->open_connection($ctx); |
191
|
0
|
|
|
|
|
|
$self->current_state(1); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
return ($self,$rc); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
0
|
0
|
|
0
|
0
|
|
sub sock { my ($self,$v)=@_; $self->transport_data()->{sock}=$v if defined($v); return $self->transport_data()->{sock}; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
## TODO (for IRIS DCHK1 + NAPTR/SRV) |
200
|
|
|
|
|
|
|
## Wrap in an eval to handle timeout (see if outer eval already for that ?) |
201
|
|
|
|
|
|
|
## Handle remote_host/port being ref array of ordered strings to try (in which case defer should be 0 probably as the list of things to try have been determined now, not later) |
202
|
|
|
|
|
|
|
## Or specify a callback to call when doing socket open to find the correct host+ports to use at that time |
203
|
|
|
|
|
|
|
sub open_socket |
204
|
|
|
|
|
|
|
{ |
205
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
206
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
207
|
0
|
|
|
|
|
|
my $type=$t->{socktype}; |
208
|
0
|
|
|
|
|
|
my $sock; |
209
|
|
|
|
|
|
|
|
210
|
0
|
0
|
|
|
|
|
my %n=( PeerAddr => $t->{remote_host}, |
211
|
|
|
|
|
|
|
PeerPort => $t->{remote_port}, |
212
|
|
|
|
|
|
|
Proto => $t->{socktype} eq 'udp'? 'udp' : 'tcp', |
213
|
|
|
|
|
|
|
Blocking => 1, |
214
|
|
|
|
|
|
|
MultiHomed => 1, |
215
|
|
|
|
|
|
|
); |
216
|
0
|
0
|
|
|
|
|
$n{LocalAddr}=$t->{local_host} if exists($t->{local_host}); |
217
|
|
|
|
|
|
|
|
218
|
0
|
0
|
0
|
|
|
|
if ($type eq 'ssl') |
|
|
0
|
|
|
|
|
|
219
|
|
|
|
|
|
|
{ |
220
|
0
|
|
|
|
|
|
$sock=IO::Socket::SSL->new(%{$t->{ssl_context}}, |
|
0
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
%n, |
222
|
|
|
|
|
|
|
); |
223
|
|
|
|
|
|
|
} elsif ($type eq 'tcp' || $type eq 'udp') |
224
|
|
|
|
|
|
|
{ |
225
|
0
|
|
|
|
|
|
$sock=IO::Socket::INET->new(%n); |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
0
|
0
|
|
|
|
|
Net::DRI::Exception->die(1,'transport/socket',6,'Unable to setup the socket for '.$t->{remote_uri}.' with error: "'.$!.($type eq 'ssl'? '" and SSL error: "'.IO::Socket::SSL::errstr().'"' : '"')) unless defined $sock; |
|
|
0
|
|
|
|
|
|
229
|
0
|
|
|
|
|
|
$sock->autoflush(1); |
230
|
0
|
|
|
|
|
|
$self->sock($sock); |
231
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{phase=>'opening',message=>'Successfully opened socket to '.$t->{remote_uri}}); |
232
|
0
|
|
|
|
|
|
return; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub send_login |
236
|
|
|
|
|
|
|
{ |
237
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
238
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
239
|
0
|
|
|
|
|
|
my $sock=$self->sock(); |
240
|
0
|
|
|
|
|
|
my $pc=$t->{pc}; |
241
|
0
|
|
|
|
|
|
my $dr; |
242
|
0
|
|
|
|
|
|
my $cltrid=$self->generate_trid($self->{logging_ctx}->{registry}); |
243
|
0
|
|
|
|
|
|
my @rs; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
## Get server greeting, if needed |
246
|
0
|
0
|
|
|
|
|
if ($ctx->{protocol}->has_action('session','connect')) |
247
|
|
|
|
|
|
|
{ |
248
|
0
|
|
|
|
|
|
my $t1=Time::HiRes::time(); |
249
|
0
|
|
|
|
|
|
$dr=$pc->read_data($self,$sock); |
250
|
0
|
|
|
|
|
|
my $t2=Time::HiRes::time(); |
251
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{trid=>$cltrid,phase=>'opening',direction=>'in',message=>$dr}); |
252
|
0
|
|
|
|
|
|
push @rs,$self->protocol_parse($ctx->{protocol},'session','connect',$dr,$cltrid,$t2-$t1); |
253
|
0
|
0
|
|
|
|
|
return Net::DRI::Util::link_rs(@rs) unless $rs[-1]->is_success(); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
0
|
0
|
|
|
|
|
return unless $ctx->{protocol}->has_action('session','login'); |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
|
foreach my $p (qw/client_login client_password/) |
259
|
|
|
|
|
|
|
{ |
260
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters($p.' must be defined') unless (exists($t->{$p}) && $t->{$p}); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
0
|
|
|
|
|
|
$cltrid=$self->generate_trid($self->{logging_ctx}->{registry}); |
264
|
|
|
|
|
|
|
|
265
|
0
|
0
|
|
|
|
|
my $login=$ctx->{protocol}->action('session','login',$cltrid,$t->{client_login},$t->{client_password},{ client_newpassword => $t->{client_newpassword}, %{$t->{protocol_data} || {}}}); ## TODO: fix last hash ref |
|
0
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'login',trid=>$cltrid,phase=>'opening',direction=>'out',message=>$login}); |
267
|
0
|
|
|
|
|
|
my $t1=Time::HiRes::time(); |
268
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception->die(0,'transport/socket',4,'Unable to send login message to '.$t->{remote_uri}) unless ($sock->connected() && $sock->print($pc->write_message($self,$login))); |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
## Verify login successful |
271
|
0
|
|
|
|
|
|
$dr=$pc->read_data($self,$sock); |
272
|
0
|
|
|
|
|
|
my $t2=Time::HiRes::time(); |
273
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'login',trid=>$cltrid,phase=>'opening',direction=>'in',message=>$dr}); |
274
|
0
|
|
|
|
|
|
push @rs,$self->protocol_parse($ctx->{protocol},'session','login',$dr,$cltrid,$t2-$t1,$login); |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
return Net::DRI::Util::link_rs(@rs); |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub send_logout |
280
|
|
|
|
|
|
|
{ |
281
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
282
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
283
|
0
|
|
|
|
|
|
my $sock=$self->sock(); |
284
|
0
|
|
|
|
|
|
my $pc=$t->{pc}; |
285
|
|
|
|
|
|
|
|
286
|
0
|
0
|
|
|
|
|
return unless $ctx->{protocol}->has_action('session','logout'); |
287
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
|
my $cltrid=$self->generate_trid($self->{logging_ctx}->{registry}); |
289
|
0
|
|
|
|
|
|
my $logout=$ctx->{protocol}->action('session','logout',$cltrid); |
290
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'logout',trid=>$cltrid,phase=>'closing',direction=>'out',message=>$logout}); |
291
|
0
|
|
|
|
|
|
my $t1=Time::HiRes::time(); |
292
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception->die(0,'transport/socket',4,'Unable to send logout message to '.$t->{remote_uri}) unless ($sock->connected() && $sock->print($pc->write_message($self,$logout))); |
293
|
0
|
|
|
|
|
|
my $dr=$pc->read_data($self,$sock); ## We expect this to throw an exception, since the server will probably cut the connection |
294
|
0
|
|
|
|
|
|
my $t2=Time::HiRes::time(); |
295
|
0
|
|
|
|
|
|
$self->time_used(time()); |
296
|
0
|
|
|
|
|
|
$t->{exchanges_done}++; |
297
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'logout',trid=>$cltrid,phase=>'closing',direction=>'in',message=>$dr}); |
298
|
0
|
|
|
|
|
|
my $rc1=$self->protocol_parse($ctx->{protocol},'session','logout',$dr,$cltrid,$t2-$t1,$logout); |
299
|
0
|
0
|
|
|
|
|
die $rc1 unless $rc1->is_success(); |
300
|
0
|
|
|
|
|
|
return $rc1; |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub open_connection |
304
|
|
|
|
|
|
|
{ |
305
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
306
|
0
|
|
|
|
|
|
$self->open_socket($ctx); |
307
|
0
|
|
|
|
|
|
my $rc=$self->send_login($ctx); |
308
|
0
|
|
|
|
|
|
$self->current_state(1); |
309
|
0
|
|
|
|
|
|
$self->time_open(time()); |
310
|
0
|
|
|
|
|
|
$self->time_used(time()); |
311
|
0
|
|
|
|
|
|
$self->transport_data()->{exchanges_done}=0; |
312
|
0
|
|
|
|
|
|
return $rc; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub ping |
316
|
|
|
|
|
|
|
{ |
317
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx,$autorecon)=@_; |
318
|
0
|
0
|
|
|
|
|
$autorecon=0 unless defined $autorecon; |
319
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
320
|
0
|
|
|
|
|
|
my $pc=$t->{pc}; |
321
|
0
|
|
|
|
|
|
my $sock=$self->sock(); |
322
|
|
|
|
|
|
|
|
323
|
0
|
0
|
|
|
|
|
return 0 unless $self->has_state(); |
324
|
0
|
0
|
|
|
|
|
return 0 unless $ctx->{protocol}->has_action('session','noop'); |
325
|
|
|
|
|
|
|
|
326
|
0
|
|
|
|
|
|
my $rc1; |
327
|
0
|
|
|
|
|
|
my $cltrid=$self->generate_trid($self->{logging_ctx}->{registry}); |
328
|
|
|
|
|
|
|
my $ok=eval |
329
|
0
|
|
|
|
|
|
{ |
330
|
0
|
|
|
0
|
|
|
local $SIG{ALRM}=sub { die 'timeout' }; |
|
0
|
|
|
|
|
|
|
331
|
0
|
|
|
|
|
|
alarm 10; |
332
|
0
|
|
|
|
|
|
my $noop=$ctx->{protocol}->action('session','noop',$cltrid); |
333
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'keepalive',trid=>$cltrid,phase=>'keepalive',direction=>'out',message=>$noop}); |
334
|
0
|
|
|
|
|
|
my $t1=Time::HiRes::time(); |
335
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception->die(0,'transport/socket',4,'Unable to send keepalive message to '.$t->{remote_uri}) unless ($sock->connected() && $sock->print($pc->write_message($self,$noop))); |
336
|
0
|
|
|
|
|
|
my $dr=$pc->read_data($self,$sock); |
337
|
0
|
|
|
|
|
|
my $t2=Time::HiRes::time(); |
338
|
0
|
|
|
|
|
|
$self->time_used(time()); |
339
|
0
|
|
|
|
|
|
$t->{exchanges_done}++; |
340
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{otype=>'session',oaction=>'keepalive',trid=>$cltrid,phase=>'keepalive',direction=>'in',message=>$dr}); |
341
|
0
|
|
|
|
|
|
$rc1=$self->protocol_parse($ctx->{protocol},'session','noop',$dr,$cltrid,$t2-$t1,$noop); |
342
|
0
|
0
|
|
|
|
|
die $rc1 unless $rc1->is_success(); |
343
|
0
|
|
|
|
|
|
1; |
344
|
|
|
|
|
|
|
}; |
345
|
0
|
|
|
|
|
|
my $err=$@; |
346
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
|
alarm 0; |
348
|
0
|
0
|
0
|
|
|
|
if (defined $ok && $ok==1) |
349
|
|
|
|
|
|
|
{ |
350
|
0
|
|
|
|
|
|
$self->current_state(1); |
351
|
|
|
|
|
|
|
} else |
352
|
|
|
|
|
|
|
{ |
353
|
0
|
|
|
|
|
|
$self->current_state(0); |
354
|
0
|
0
|
0
|
|
|
|
$rc1=$err if defined $err && Net::DRI::Util::is_class($err,'Net::DRI::Protocol::ResultStatus'); |
355
|
0
|
0
|
|
|
|
|
if ($autorecon) |
356
|
|
|
|
|
|
|
{ |
357
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',{},{phase=>'keepalive',message=>'Reopening connection to '.$t->{remote_uri}.' because ping failed and asked to auto-reconnect'}); |
358
|
0
|
|
|
|
|
|
my $rc2=$self->open_connection($ctx); |
359
|
0
|
0
|
|
|
|
|
$rc1=defined $rc1 ? Net::DRI::Util::link_rs($rc1,$rc2) : $rc2; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
0
|
0
|
|
|
|
|
return defined $rc1 ? $rc1 : Net::DRI::Protocol::ResultStatus->new_error('COMMAND_FAILED_CLOSING','ping failed, no auto-reconnect'); |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub close_socket |
367
|
|
|
|
|
|
|
{ |
368
|
0
|
|
|
0
|
0
|
|
my ($self)=@_; |
369
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
370
|
0
|
|
|
|
|
|
$self->sock()->close(); |
371
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',{},{phase=>'closing',message=>'Successfully closed socket for '.$t->{remote_uri}}); |
372
|
0
|
|
|
|
|
|
$self->sock(undef); |
373
|
0
|
|
|
|
|
|
return; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub close_connection |
377
|
|
|
|
|
|
|
{ |
378
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
379
|
0
|
|
|
|
|
|
$self->send_logout($ctx); |
380
|
0
|
|
|
|
|
|
$self->close_socket(); |
381
|
0
|
|
|
|
|
|
$self->current_state(0); |
382
|
0
|
|
|
|
|
|
return; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub end |
386
|
|
|
|
|
|
|
{ |
387
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx)=@_; |
388
|
0
|
0
|
|
|
|
|
if ($self->current_state()) |
389
|
|
|
|
|
|
|
{ |
390
|
|
|
|
|
|
|
eval |
391
|
0
|
|
|
|
|
|
{ |
392
|
0
|
|
|
0
|
|
|
local $SIG{ALRM}=sub { die 'timeout' }; |
|
0
|
|
|
|
|
|
|
393
|
0
|
|
|
|
|
|
alarm 10; |
394
|
0
|
|
|
|
|
|
$self->close_connection($ctx); |
395
|
|
|
|
|
|
|
}; |
396
|
0
|
|
|
|
|
|
alarm 0; ## since close_connection may die, this must be outside of eval to be executed in all cases |
397
|
|
|
|
|
|
|
} |
398
|
0
|
|
|
|
|
|
return; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#################################################################################################### |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub send ## no critic (Subroutines::ProhibitBuiltinHomonyms) |
404
|
|
|
|
|
|
|
{ |
405
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx,$tosend,$count)=@_; |
406
|
|
|
|
|
|
|
## We do a very crude error handling : if first send fails, we reset connection. |
407
|
|
|
|
|
|
|
## Thus if you put retry=>2 when creating this object, the connection will be re-established and the message resent |
408
|
0
|
|
|
0
|
|
|
return $self->SUPER::send($ctx,$tosend,\&_print,sub { shift->current_state(0) },$count); |
|
0
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub _print ## here we are sure open_connection() was called before |
412
|
|
|
|
|
|
|
{ |
413
|
0
|
|
|
0
|
|
|
my ($self,$count,$tosend,$ctx)=@_; |
414
|
0
|
|
|
|
|
|
my $pc=$self->transport_data('pc'); |
415
|
0
|
|
|
|
|
|
my $sock=$self->sock(); |
416
|
0
|
0
|
|
|
|
|
my $m=($self->transport_data('socktype') eq 'udp')? 'send' : 'print'; |
417
|
0
|
0
|
0
|
|
|
|
Net::DRI::Exception->die(0,'transport/socket',4,'Unable to send message to '.$self->transport_data('remote_uri').' because of error: '.$!) unless (($m ne 'print' || $sock->connected()) && $sock->$m($pc->write_message($self,$tosend))); |
|
|
|
0
|
|
|
|
|
418
|
0
|
|
|
|
|
|
return 1; ## very important |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
sub receive |
422
|
|
|
|
|
|
|
{ |
423
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx,$count)=@_; |
424
|
0
|
|
|
|
|
|
return $self->SUPER::receive($ctx,\&_get,undef,$count); |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub _get |
428
|
|
|
|
|
|
|
{ |
429
|
0
|
|
|
0
|
|
|
my ($self,$count,$ctx)=@_; |
430
|
0
|
|
|
|
|
|
my $t=$self->transport_data(); |
431
|
0
|
|
|
|
|
|
my $sock=$self->sock(); |
432
|
0
|
|
|
|
|
|
my $pc=$t->{pc}; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
## Answer |
435
|
0
|
|
|
|
|
|
my $dr=$pc->read_data($self,$sock); |
436
|
0
|
|
|
|
|
|
$t->{exchanges_done}++; |
437
|
0
|
0
|
0
|
|
|
|
if ($t->{exchanges_done}==$t->{close_after} && $self->has_state() && $self->current_state()) |
|
|
|
0
|
|
|
|
|
438
|
|
|
|
|
|
|
{ |
439
|
0
|
|
|
|
|
|
$self->log_output('notice','transport',$ctx,{phase=>'closing',message=>'Due to maximum number of exchanges reached, closing connection to '.$t->{remote_uri}}); |
440
|
0
|
|
|
|
|
|
$self->close_connection($ctx); |
441
|
|
|
|
|
|
|
} |
442
|
0
|
|
|
|
|
|
return $dr; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
sub try_again |
446
|
|
|
|
|
|
|
{ |
447
|
0
|
|
|
0
|
0
|
|
my ($self,$ctx,$po,$err,$count,$istimeout,$step,$rpause,$rtimeout)=@_; |
448
|
0
|
0
|
|
|
|
|
if ($step==0) ## sending not already done, hence error during send |
449
|
|
|
|
|
|
|
{ |
450
|
0
|
|
|
|
|
|
$self->current_state(0); |
451
|
0
|
|
|
|
|
|
return 1; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
## We do a more agressive retry procedure in case of udp (that is IRIS basically) |
455
|
|
|
|
|
|
|
## See RFC4993 section 4 |
456
|
0
|
0
|
0
|
|
|
|
if ($step==1 && $istimeout==1 && $self->transport_data()->{socktype} eq 'udp') |
|
|
|
0
|
|
|
|
|
457
|
|
|
|
|
|
|
{ |
458
|
0
|
|
|
|
|
|
$self->log_output('debug','transport',$ctx,{phase=>'active',message=>sprintf('In try_again, currently: pause=%f timeout=%f',$$rpause,$$rtimeout)}); |
459
|
0
|
|
|
|
|
|
$$rtimeout=2*$$rtimeout; |
460
|
0
|
|
|
|
|
|
$$rpause+=rand(1+int($$rpause/2)); |
461
|
0
|
|
|
|
|
|
$self->log_output('debug','transport',$ctx,{phase=>'active',message=>sprintf('In try_again, new values: pause=%f timeout=%f',$$rpause,$$rtimeout)}); |
462
|
0
|
|
|
|
|
|
return 1; ## we will retry |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
0
|
|
|
|
|
|
return 0; ## we do not handle other cases, hence no retry and fatal error |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
#################################################################################################### |
470
|
|
|
|
|
|
|
1; |