line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
########################################################################### |
3
|
|
|
|
|
|
|
# package Net::SIP::Dispatcher |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Manages the sending of SIP packets to the legs (and finding out which |
6
|
|
|
|
|
|
|
# leg can be used) and the receiving of SIP packets and forwarding to |
7
|
|
|
|
|
|
|
# the upper layer. |
8
|
|
|
|
|
|
|
# Handles retransmits |
9
|
|
|
|
|
|
|
########################################################################### |
10
|
|
|
|
|
|
|
|
11
|
43
|
|
|
43
|
|
309
|
use strict; |
|
43
|
|
|
|
|
70
|
|
|
43
|
|
|
|
|
1335
|
|
12
|
43
|
|
|
43
|
|
177
|
use warnings; |
|
43
|
|
|
|
|
60
|
|
|
43
|
|
|
|
|
1936
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Net::SIP::Dispatcher; |
15
|
|
|
|
|
|
|
use fields ( |
16
|
|
|
|
|
|
|
# interface to outside |
17
|
43
|
|
|
|
|
194
|
'receiver', # callback into upper layer |
18
|
|
|
|
|
|
|
'legs', # \@list of Net::SIP::Legs managed by dispatcher |
19
|
|
|
|
|
|
|
'eventloop', # Net::SIP::Dispatcher::Eventloop or similar |
20
|
|
|
|
|
|
|
'outgoing_proxy', # optional fixed outgoing proxy |
21
|
|
|
|
|
|
|
'domain2proxy', # optional mapping between SIP domains and proxies (otherwise use DNS) |
22
|
|
|
|
|
|
|
# internals |
23
|
|
|
|
|
|
|
'do_retransmits', # flag if retransmits will be done (false for stateless proxy) |
24
|
|
|
|
|
|
|
'queue', # \@list of outstanding Net::SIP::Dispatcher::Packet |
25
|
|
|
|
|
|
|
'response_cache', # Cache of responses, used to reply to retransmits |
26
|
|
|
|
|
|
|
'disp_expire', # expire/retransmit timer |
27
|
|
|
|
|
|
|
'dnsresolv', # optional external DNS resolver |
28
|
43
|
|
|
43
|
|
183
|
); |
|
43
|
|
|
|
|
73
|
|
29
|
|
|
|
|
|
|
|
30
|
43
|
|
|
43
|
|
21677
|
use Net::SIP::Leg; |
|
43
|
|
|
|
|
135
|
|
|
43
|
|
|
|
|
1244
|
|
31
|
43
|
|
|
43
|
|
540
|
use Net::SIP::Util ':all'; |
|
43
|
|
|
|
|
274
|
|
|
43
|
|
|
|
|
7409
|
|
32
|
43
|
|
|
43
|
|
260
|
use Net::SIP::Dispatcher::Eventloop; |
|
43
|
|
|
|
|
75
|
|
|
43
|
|
|
|
|
1756
|
|
33
|
43
|
|
|
43
|
|
263
|
use Errno qw(EHOSTUNREACH ETIMEDOUT ENOPROTOOPT); |
|
43
|
|
|
|
|
67
|
|
|
43
|
|
|
|
|
1675
|
|
34
|
43
|
|
|
43
|
|
206
|
use IO::Socket; |
|
43
|
|
|
|
|
69
|
|
|
43
|
|
|
|
|
336
|
|
35
|
43
|
|
|
43
|
|
18076
|
use List::Util 'first'; |
|
43
|
|
|
|
|
103
|
|
|
43
|
|
|
|
|
2269
|
|
36
|
43
|
|
|
43
|
|
260
|
use Hash::Util 'lock_ref_keys'; |
|
43
|
|
|
|
|
78
|
|
|
43
|
|
|
|
|
202
|
|
37
|
43
|
|
|
43
|
|
1948
|
use Carp 'croak'; |
|
43
|
|
|
|
|
68
|
|
|
43
|
|
|
|
|
1536
|
|
38
|
43
|
|
|
43
|
|
229
|
use Net::SIP::Debug; |
|
43
|
|
|
|
|
81
|
|
|
43
|
|
|
|
|
356
|
|
39
|
43
|
|
|
43
|
|
252
|
use Scalar::Util 'weaken'; |
|
43
|
|
|
|
|
101
|
|
|
43
|
|
|
|
|
2012
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# The maximum priority value in SRV records is 0xffff and the lowest priority |
42
|
|
|
|
|
|
|
# value is considered the best. Make undefined priority higher so that it gets |
43
|
|
|
|
|
|
|
# considered as last option. |
44
|
43
|
|
|
43
|
|
273
|
use constant SRV_PRIO_UNDEF => 0x10000; |
|
43
|
|
|
|
|
102
|
|
|
43
|
|
|
|
|
72729
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
########################################################################### |
47
|
|
|
|
|
|
|
# create new dispatcher |
48
|
|
|
|
|
|
|
# Args: ($class,$legs,$eventloop;%args) |
49
|
|
|
|
|
|
|
# $legs: \@array, see add_leg() |
50
|
|
|
|
|
|
|
# $eventloop: Net::SIP::Dispatcher::Eventloop or similar |
51
|
|
|
|
|
|
|
# %args: |
52
|
|
|
|
|
|
|
# outgoing_proxy: optional outgoing proxy (ip:port) |
53
|
|
|
|
|
|
|
# do_retransmits: set if the dispatcher has to handle retransmits by itself |
54
|
|
|
|
|
|
|
# defaults to true |
55
|
|
|
|
|
|
|
# domain2proxy: mappings { domain => proxy } if a fixed proxy is used |
56
|
|
|
|
|
|
|
# for specific domains, otherwise lookup will be done per DNS |
57
|
|
|
|
|
|
|
# proxy can be ip,ip:port or \@list of hash with keys prio, proto, host, |
58
|
|
|
|
|
|
|
# port and family like in the DNS SRV record |
59
|
|
|
|
|
|
|
# with special domain '*' a default can be specified, so that DNS |
60
|
|
|
|
|
|
|
# will not be used at all |
61
|
|
|
|
|
|
|
# dnsresolv: DNS resolver function with interface sub->(type,domain,callback) |
62
|
|
|
|
|
|
|
# which then calls callback->(\@result) with @result being a list of |
63
|
|
|
|
|
|
|
# [ 'SRV',prio,target,port], ['A',ip,name], ['AAAA',ip,name] |
64
|
|
|
|
|
|
|
# Returns: $self |
65
|
|
|
|
|
|
|
########################################################################### |
66
|
|
|
|
|
|
|
sub new { |
67
|
57
|
|
|
57
|
1
|
748
|
my ($class,$legs,$eventloop,%args) = @_; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
my ($outgoing_proxy,$do_retransmits,$domain2proxy,$dnsresolv) = delete |
70
|
57
|
|
|
|
|
276
|
@args{qw( outgoing_proxy do_retransmits domain2proxy dnsresolv)}; |
71
|
57
|
50
|
|
|
|
264
|
die "bad args: ".join( ' ',keys %args ) if %args; |
72
|
|
|
|
|
|
|
|
73
|
57
|
|
33
|
|
|
248
|
$eventloop ||= Net::SIP::Dispatcher::Eventloop->new; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# normalize domain2proxy so that its the same format one gets from |
76
|
|
|
|
|
|
|
# the SRV record |
77
|
57
|
|
100
|
|
|
572
|
$domain2proxy ||= {}; |
78
|
57
|
|
|
|
|
239
|
foreach ( values %$domain2proxy ) { |
79
|
30
|
50
|
|
|
|
107
|
if ( ref($_) ) { |
80
|
|
|
|
|
|
|
# should be \@list of [ prio,proto,ip,port,?family ] |
81
|
|
|
|
|
|
|
} else { |
82
|
30
|
50
|
|
|
|
134
|
my ($proto,$host,$port,$family) = sip_uri2sockinfo($_) |
83
|
|
|
|
|
|
|
or croak( "invalid entry in domain2proxy: $_" ); |
84
|
30
|
0
|
0
|
|
|
111
|
$port ||= $proto && $proto eq 'tls' ? 5061:5060; |
|
|
|
33
|
|
|
|
|
85
|
30
|
50
|
|
|
|
124
|
$_ = [ map { lock_ref_keys({ |
|
48
|
100
|
|
|
|
974
|
|
86
|
|
|
|
|
|
|
prio => SRV_PRIO_UNDEF, |
87
|
|
|
|
|
|
|
proto => $_, |
88
|
|
|
|
|
|
|
host => $host, |
89
|
|
|
|
|
|
|
addr => $family ? $host : undef, |
90
|
|
|
|
|
|
|
port => $port, |
91
|
|
|
|
|
|
|
family => $family |
92
|
|
|
|
|
|
|
}) } $proto ? ($proto) : ('udp','tcp') ]; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
57
|
|
|
|
|
498
|
my $self = fields::new($class); |
97
|
57
|
50
|
|
|
|
8024
|
%$self = ( |
98
|
|
|
|
|
|
|
legs => [], |
99
|
|
|
|
|
|
|
queue => [], |
100
|
|
|
|
|
|
|
outgoing_proxy => undef, |
101
|
|
|
|
|
|
|
response_cache => {}, |
102
|
|
|
|
|
|
|
do_retransmits => defined( $do_retransmits ) ? $do_retransmits : 1, |
103
|
|
|
|
|
|
|
eventloop => $eventloop, |
104
|
|
|
|
|
|
|
domain2proxy => $domain2proxy, |
105
|
|
|
|
|
|
|
dnsresolv => $dnsresolv, |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
|
108
|
57
|
|
|
|
|
1870
|
$self->add_leg( @$legs ); |
109
|
|
|
|
|
|
|
|
110
|
57
|
50
|
|
|
|
217
|
$self->outgoing_proxy($outgoing_proxy) if $outgoing_proxy; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# regularly prune queue |
113
|
|
|
|
|
|
|
my $sub = sub { |
114
|
174
|
|
|
174
|
|
790
|
my ($self,$timer) = @_; |
115
|
174
|
50
|
|
|
|
1074
|
if ( $self ) { |
116
|
174
|
|
|
|
|
1007
|
$self->queue_expire( $self->{eventloop}->looptime ); |
117
|
|
|
|
|
|
|
} else { |
118
|
0
|
|
|
|
|
0
|
$timer->cancel; |
119
|
|
|
|
|
|
|
} |
120
|
57
|
|
|
|
|
1143
|
}; |
121
|
57
|
|
|
|
|
432
|
my $cb = [ $sub,$self ]; |
122
|
57
|
|
|
|
|
717
|
weaken( $cb->[1] ); |
123
|
57
|
|
|
|
|
1192
|
$self->{disp_expire} = $self->add_timer( 1,$cb,1,'disp_expire' ); |
124
|
|
|
|
|
|
|
|
125
|
57
|
|
|
|
|
246
|
return $self; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
########################################################################### |
129
|
|
|
|
|
|
|
# get or set outgoing proxy |
130
|
|
|
|
|
|
|
# Args: ($self;$proxy) |
131
|
|
|
|
|
|
|
# $proxy: optional new proxy or undef if proxy should be none |
132
|
|
|
|
|
|
|
# Returns: |
133
|
|
|
|
|
|
|
# $proxy: current setting, i.e. after possible update |
134
|
|
|
|
|
|
|
########################################################################### |
135
|
|
|
|
|
|
|
sub outgoing_proxy { |
136
|
0
|
|
|
0
|
1
|
0
|
my Net::SIP::Dispatcher $self = shift; |
137
|
0
|
0
|
|
|
|
0
|
return $self->{outgoing_proxy} if ! @_; |
138
|
0
|
|
|
|
|
0
|
my $outgoing_proxy = shift; |
139
|
0
|
|
0
|
|
|
0
|
my $leg = $self->_find_leg4addr( $outgoing_proxy ) |
140
|
|
|
|
|
|
|
|| die "cannot find leg for destination $outgoing_proxy"; |
141
|
0
|
|
|
|
|
0
|
$self->{outgoing_proxy} = $outgoing_proxy; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
########################################################################### |
146
|
|
|
|
|
|
|
# get or set the event loop |
147
|
|
|
|
|
|
|
# Args: ($self;$loop) |
148
|
|
|
|
|
|
|
# $loop: optional new loop |
149
|
|
|
|
|
|
|
# Returns: |
150
|
|
|
|
|
|
|
# $loop: current setting, i.e. after possible update |
151
|
|
|
|
|
|
|
########################################################################### |
152
|
|
|
|
|
|
|
sub loop { |
153
|
0
|
|
|
0
|
0
|
0
|
my Net::SIP::Dispatcher $self = shift; |
154
|
0
|
0
|
|
|
|
0
|
return $self->{eventloop} if ! @_; |
155
|
0
|
|
|
|
|
0
|
$self->{eventloop} = shift; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
########################################################################### |
160
|
|
|
|
|
|
|
# set receiver, e.g the upper layer which gets the incoming packets |
161
|
|
|
|
|
|
|
# received by the dispatcher |
162
|
|
|
|
|
|
|
# Args: ($self,$receiver) |
163
|
|
|
|
|
|
|
# $receiver: object which has receive( Net::SIP::Leg,Net::SIP::Packet ) |
164
|
|
|
|
|
|
|
# method to handle incoming SIP packets or callback |
165
|
|
|
|
|
|
|
# might be undef - in this case the existing receiver will be removed |
166
|
|
|
|
|
|
|
# Returns: NONE |
167
|
|
|
|
|
|
|
########################################################################### |
168
|
|
|
|
|
|
|
sub set_receiver { |
169
|
59
|
|
|
59
|
1
|
120
|
my Net::SIP::Dispatcher $self = shift; |
170
|
59
|
50
|
|
|
|
247
|
if ( my $receiver = shift ) { |
171
|
59
|
100
|
|
|
|
232
|
if ( my $sub = UNIVERSAL::can($receiver,'receive' )) { |
172
|
|
|
|
|
|
|
# Object with method receive() |
173
|
5
|
|
|
|
|
12
|
$receiver = [ $sub,$receiver ] |
174
|
|
|
|
|
|
|
} |
175
|
59
|
|
|
|
|
1096
|
$self->{receiver} = $receiver; |
176
|
|
|
|
|
|
|
} else { |
177
|
|
|
|
|
|
|
# remove receiver |
178
|
|
|
|
|
|
|
$self->{receiver} = undef |
179
|
0
|
|
|
|
|
0
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
########################################################################### |
184
|
|
|
|
|
|
|
# adds a leg to the dispatcher |
185
|
|
|
|
|
|
|
# Args: ($self,@legs) |
186
|
|
|
|
|
|
|
# @legs: can be sockets, \%args for constructing or already |
187
|
|
|
|
|
|
|
# objects of class Net::SIP::Leg |
188
|
|
|
|
|
|
|
# Returns: NONE |
189
|
|
|
|
|
|
|
########################################################################### |
190
|
|
|
|
|
|
|
sub add_leg { |
191
|
111
|
|
|
111
|
1
|
238
|
my Net::SIP::Dispatcher $self = shift; |
192
|
111
|
|
|
|
|
275
|
my $legs = $self->{legs}; |
193
|
111
|
|
|
|
|
514
|
foreach my $arg (@_) { |
194
|
|
|
|
|
|
|
|
195
|
59
|
|
|
|
|
108
|
my $leg; |
196
|
|
|
|
|
|
|
# if it is not a leg yet create one based |
197
|
|
|
|
|
|
|
# on the arguments |
198
|
59
|
50
|
|
|
|
1364
|
if ( UNIVERSAL::isa( $arg,'Net::SIP::Leg' )) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# already a leg |
200
|
59
|
|
|
|
|
136
|
$leg = $arg; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
} elsif ( UNIVERSAL::isa( $arg,'IO::Handle' )) { |
203
|
|
|
|
|
|
|
# create from socket |
204
|
0
|
|
|
|
|
0
|
$leg = Net::SIP::Leg->new( sock => $arg ); |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
} elsif ( UNIVERSAL::isa( $arg,'HASH' )) { |
207
|
|
|
|
|
|
|
# create from %args |
208
|
0
|
|
|
|
|
0
|
$leg = Net::SIP::Leg->new( %$arg ); |
209
|
|
|
|
|
|
|
} else { |
210
|
0
|
|
|
|
|
0
|
croak "invalid spec for leg: $arg"; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
59
|
|
|
|
|
129
|
push @$legs, $leg; |
214
|
|
|
|
|
|
|
|
215
|
59
|
50
|
|
|
|
791
|
if (my $socketpool = $leg->socketpool) { |
216
|
|
|
|
|
|
|
my $cb = sub { |
217
|
|
|
|
|
|
|
# don't crash Dispatcher on bad or unexpected packets |
218
|
209
|
50
|
|
209
|
|
452
|
eval { |
219
|
209
|
|
|
|
|
592
|
my ($self,$leg,$packet,$from) = @_; |
220
|
209
|
50
|
|
|
|
653
|
$self || return; |
221
|
|
|
|
|
|
|
|
222
|
209
|
50
|
|
|
|
1478
|
($packet,$from) = $leg->receive($packet,$from) or return; |
223
|
|
|
|
|
|
|
|
224
|
209
|
100
|
|
|
|
1720
|
if ($packet->is_request) { |
225
|
|
|
|
|
|
|
# add received and rport to top via |
226
|
|
|
|
|
|
|
$packet->scan_header( via => [ sub { |
227
|
70
|
|
|
|
|
184
|
my ($vref,$hdr) = @_; |
228
|
70
|
50
|
|
|
|
313
|
return if $$vref++; |
229
|
70
|
|
|
|
|
546
|
my ($d,$h) = sip_hdrval2parts(via => $hdr->{value}); |
230
|
70
|
50
|
|
|
|
1069
|
my ($host,$port) = $d =~m{^SIP/2\S+\s+(\S+)$} |
231
|
|
|
|
|
|
|
? ip_string2parts($1):(); |
232
|
70
|
|
|
|
|
162
|
my %nh; |
233
|
70
|
50
|
33
|
|
|
423
|
if ( exists $h->{rport} and ! defined $h->{rport}) { |
234
|
0
|
|
|
|
|
0
|
$nh{rport} = $from->{port}; |
235
|
|
|
|
|
|
|
} |
236
|
70
|
50
|
|
|
|
794
|
if ($host ne $from->{addr}) { |
|
|
50
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# either from.addr is the addr for host or we |
238
|
|
|
|
|
|
|
# had a different IP address in the via header |
239
|
0
|
|
|
|
|
0
|
$nh{received} = $from->{addr}; |
240
|
|
|
|
|
|
|
} elsif ($nh{rport}) { |
241
|
|
|
|
|
|
|
# required because rport was set |
242
|
0
|
|
|
|
|
0
|
$nh{received} = $from->{addr}; |
243
|
|
|
|
|
|
|
} |
244
|
70
|
50
|
|
|
|
429
|
if (%nh) { |
245
|
0
|
|
|
|
|
0
|
$hdr->{value} = sip_parts2hdrval('via',$d,{ %$h,%nh}); |
246
|
0
|
|
|
|
|
0
|
$hdr->set_modified; |
247
|
|
|
|
|
|
|
} |
248
|
70
|
|
|
|
|
1673
|
}, \( my $cvia )]); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# handle received packet |
252
|
209
|
|
|
|
|
3376
|
$self->receive( $packet,$leg,$from ); |
253
|
209
|
|
|
|
|
2854
|
1; |
254
|
|
|
|
|
|
|
} or DEBUG(1,"dispatcher croaked: $@"); |
255
|
59
|
|
|
|
|
1339
|
}; |
256
|
59
|
|
|
|
|
260
|
$cb = [ $cb,$self,$leg ]; |
257
|
59
|
|
|
|
|
282
|
weaken($cb->[1]); |
258
|
59
|
|
|
|
|
150
|
weaken($cb->[2]); |
259
|
59
|
|
|
|
|
375
|
$socketpool->attach_eventloop($self->{eventloop}, $cb); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
########################################################################### |
265
|
|
|
|
|
|
|
# remove a leg from the dispatcher |
266
|
|
|
|
|
|
|
# Args: ($self,@legs) |
267
|
|
|
|
|
|
|
# @legs: Net::SIP::Leg objects |
268
|
|
|
|
|
|
|
# Returns: NONE |
269
|
|
|
|
|
|
|
########################################################################### |
270
|
|
|
|
|
|
|
sub remove_leg { |
271
|
53
|
|
|
53
|
1
|
127
|
my Net::SIP::Dispatcher $self = shift; |
272
|
53
|
|
|
|
|
137
|
my $legs = $self->{legs}; |
273
|
53
|
|
|
|
|
442
|
foreach my $leg (@_) { |
274
|
53
|
|
|
|
|
180
|
@$legs = grep { $_ != $leg } @$legs; |
|
53
|
|
|
|
|
248
|
|
275
|
53
|
50
|
|
|
|
248
|
if ( my $pool = $leg->socketpool ) { |
276
|
53
|
|
|
|
|
338
|
$pool->attach_eventloop(); |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
########################################################################### |
282
|
|
|
|
|
|
|
# find legs matching specific criterias |
283
|
|
|
|
|
|
|
# Args: ($self,%args) |
284
|
|
|
|
|
|
|
# %args: Hash with some of these keys |
285
|
|
|
|
|
|
|
# addr: leg must match addr |
286
|
|
|
|
|
|
|
# port: leg must match port |
287
|
|
|
|
|
|
|
# proto: leg must match proto |
288
|
|
|
|
|
|
|
# sub: $sub->($leg) must return true |
289
|
|
|
|
|
|
|
# Returns: @legs |
290
|
|
|
|
|
|
|
# @legs: all Legs matching the criteria |
291
|
|
|
|
|
|
|
# Comment: |
292
|
|
|
|
|
|
|
# if no criteria given it will return all legs |
293
|
|
|
|
|
|
|
########################################################################### |
294
|
|
|
|
|
|
|
sub get_legs { |
295
|
157
|
|
|
157
|
1
|
429
|
my Net::SIP::Dispatcher $self = shift; |
296
|
157
|
100
|
|
|
|
436
|
return @{ $self->{legs} } if ! @_; # shortcut |
|
155
|
|
|
|
|
738
|
|
297
|
|
|
|
|
|
|
|
298
|
2
|
|
|
|
|
6
|
my %args = @_; |
299
|
2
|
|
|
|
|
10
|
my @rv; |
300
|
2
|
|
|
|
|
3
|
foreach my $leg (@{ $self->{legs} }) { |
|
2
|
|
|
|
|
4
|
|
301
|
2
|
50
|
|
|
|
10
|
push @rv,$leg if $leg->match(\%args); |
302
|
|
|
|
|
|
|
} |
303
|
2
|
|
|
|
|
8
|
return @rv; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
########################################################################### |
308
|
|
|
|
|
|
|
# map leg to index in list of legs |
309
|
|
|
|
|
|
|
# Args: @legs,[\$dict] |
310
|
|
|
|
|
|
|
# @legs: list of legs |
311
|
|
|
|
|
|
|
# $dict: string representation of dictionary, used in i2leg and others |
312
|
|
|
|
|
|
|
# to make sure that it the indices come from the same list of legs. |
313
|
|
|
|
|
|
|
# Will be set if given |
314
|
|
|
|
|
|
|
# Returns: @ilegs |
315
|
|
|
|
|
|
|
# @ilegs: index of each of @legs in dispatcher, -1 if not found |
316
|
|
|
|
|
|
|
########################################################################### |
317
|
|
|
|
|
|
|
sub legs2i { |
318
|
7
|
|
|
7
|
0
|
11
|
my Net::SIP::Dispatcher $self = shift; |
319
|
7
|
|
|
|
|
10
|
my $legs = $self->{legs}; |
320
|
7
|
50
|
|
|
|
16
|
if (ref($_[-1]) eq 'SCALAR') { |
321
|
7
|
|
|
|
|
19
|
my $dict = pop @_; |
322
|
7
|
|
|
|
|
13
|
$$dict = join("|",map { $_->key } @$legs); |
|
19
|
|
|
|
|
57
|
|
323
|
|
|
|
|
|
|
} |
324
|
7
|
|
|
|
|
12
|
my @result; |
325
|
7
|
|
|
|
|
15
|
for(@_) { |
326
|
14
|
|
|
|
|
14
|
my $i; |
327
|
14
|
|
|
|
|
28
|
for($i=$#$legs;$i>=0;$i--) { |
328
|
31
|
100
|
|
|
|
71
|
last if $legs->[$i] == $_; |
329
|
|
|
|
|
|
|
} |
330
|
14
|
|
|
|
|
23
|
push @result,$i; |
331
|
|
|
|
|
|
|
} |
332
|
7
|
|
|
|
|
18
|
return @result; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
########################################################################### |
336
|
|
|
|
|
|
|
# map index to leg in list of legs |
337
|
|
|
|
|
|
|
# Args: @ilegs,[\$dict] |
338
|
|
|
|
|
|
|
# @ilegs: list of leg indices |
339
|
|
|
|
|
|
|
# $dict: optional string representation of dictionary, will return () |
340
|
|
|
|
|
|
|
# if $dict does not match current legs and order in dispatcher |
341
|
|
|
|
|
|
|
# Returns: @legs |
342
|
|
|
|
|
|
|
# @legs: list of legs matching indices |
343
|
|
|
|
|
|
|
########################################################################### |
344
|
|
|
|
|
|
|
sub i2legs { |
345
|
0
|
|
|
0
|
0
|
0
|
my Net::SIP::Dispatcher $self = shift; |
346
|
0
|
|
|
|
|
0
|
my $legs = $self->{legs}; |
347
|
0
|
0
|
|
|
|
0
|
if (ref($_[-1])) { |
348
|
0
|
0
|
|
|
|
0
|
return if ${pop(@_)} ne join("|",map { $_->key } @$legs); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
349
|
|
|
|
|
|
|
} |
350
|
0
|
|
|
|
|
0
|
return @{$legs}[@_]; |
|
0
|
|
|
|
|
0
|
|
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
########################################################################### |
354
|
|
|
|
|
|
|
# add timer |
355
|
|
|
|
|
|
|
# propagates to add_timer of eventloop |
356
|
|
|
|
|
|
|
# Args: ($self,$when,$cb,$repeat) |
357
|
|
|
|
|
|
|
# $when: when callback gets called, can be absolute time (epoch, time_t) |
358
|
|
|
|
|
|
|
# or relative time (seconds) |
359
|
|
|
|
|
|
|
# $cb: callback |
360
|
|
|
|
|
|
|
# $repeat: after how much seconds it gets repeated (default 0, e.g never) |
361
|
|
|
|
|
|
|
# Returns: $timer |
362
|
|
|
|
|
|
|
# $timer: Timer object, has method cancel for canceling timer |
363
|
|
|
|
|
|
|
########################################################################### |
364
|
|
|
|
|
|
|
sub add_timer { |
365
|
124
|
|
|
124
|
1
|
388
|
my Net::SIP::Dispatcher $self = shift; |
366
|
124
|
|
|
|
|
1345
|
return $self->{eventloop}->add_timer( @_ ); |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
########################################################################### |
370
|
|
|
|
|
|
|
# initiate delivery of a packet, e.g. put packet into delivery queue |
371
|
|
|
|
|
|
|
# Args: ($self,$packet,%more_args) |
372
|
|
|
|
|
|
|
# $packet: Net::SIP::Packet which needs to be delivered |
373
|
|
|
|
|
|
|
# %more_args: hash with some of the following keys |
374
|
|
|
|
|
|
|
# id: id for packet, used in cancel_delivery |
375
|
|
|
|
|
|
|
# callback: [ \&sub,@arg ] for calling back on definite delivery |
376
|
|
|
|
|
|
|
# success (tcp only) or error (timeout,no route,...) |
377
|
|
|
|
|
|
|
# leg: specify outgoing leg, needed for responses |
378
|
|
|
|
|
|
|
# dst_addr: specify outgoing addr as hash with keys |
379
|
|
|
|
|
|
|
# proto,addr,port,family,host. Needed for responses. |
380
|
|
|
|
|
|
|
# do_retransmits: if retransmits should be done, default from |
381
|
|
|
|
|
|
|
# global value (see new()) |
382
|
|
|
|
|
|
|
# Returns: NONE |
383
|
|
|
|
|
|
|
# Comment: no return value, but die()s on errors |
384
|
|
|
|
|
|
|
########################################################################### |
385
|
|
|
|
|
|
|
sub deliver { |
386
|
196
|
|
|
196
|
1
|
390
|
my Net::SIP::Dispatcher $self = shift; |
387
|
196
|
|
|
|
|
1360
|
my ($packet,%more_args) = @_; |
388
|
196
|
|
|
|
|
752
|
my $now = delete $more_args{now}; |
389
|
196
|
|
|
|
|
478
|
my $do_retransmits = delete $more_args{do_retransmits}; |
390
|
196
|
100
|
|
|
|
819
|
$do_retransmits = $self->{do_retransmits} if !defined $do_retransmits; |
391
|
|
|
|
|
|
|
|
392
|
196
|
|
|
|
|
1277
|
DEBUG( 100,"deliver $packet" ); |
393
|
|
|
|
|
|
|
|
394
|
196
|
100
|
|
|
|
1904
|
if ( $packet->is_response ) { |
395
|
|
|
|
|
|
|
# cache response for 32 sec (64*T1) |
396
|
76
|
50
|
|
|
|
208
|
if ( $do_retransmits ) { |
397
|
|
|
|
|
|
|
my $cid = join( "\0", |
398
|
76
|
|
|
|
|
192
|
map { $packet->get_header($_) } |
|
304
|
|
|
|
|
788
|
|
399
|
|
|
|
|
|
|
qw( cseq call-id from to ) |
400
|
|
|
|
|
|
|
); |
401
|
76
|
|
33
|
|
|
1232
|
$self->{response_cache}{$cid} = { |
402
|
|
|
|
|
|
|
packet => $packet, |
403
|
|
|
|
|
|
|
expire => ( $now ||= time()) +32 |
404
|
|
|
|
|
|
|
}; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
196
|
|
|
|
|
2627
|
my $new_entry = Net::SIP::Dispatcher::Packet->new( |
409
|
|
|
|
|
|
|
packet => $packet, |
410
|
|
|
|
|
|
|
%more_args |
411
|
|
|
|
|
|
|
); |
412
|
|
|
|
|
|
|
|
413
|
196
|
100
|
|
|
|
1119
|
$new_entry->prepare_retransmits( $now ) if $do_retransmits; |
414
|
|
|
|
|
|
|
|
415
|
196
|
|
|
|
|
359
|
push @{ $self->{queue}}, $new_entry; |
|
196
|
|
|
|
|
629
|
|
416
|
196
|
|
|
|
|
1106
|
$self->__deliver( $new_entry ); |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
########################################################################### |
420
|
|
|
|
|
|
|
# cancel delivery of all packets with specific id |
421
|
|
|
|
|
|
|
# Args: ($self,$typ?,$id) |
422
|
|
|
|
|
|
|
# $typ: what to cancel, e.g. 'id','callid' or 'qentry', optional, |
423
|
|
|
|
|
|
|
# defaults to 'id' if $id is not ref or 'qentry' if $id is ref |
424
|
|
|
|
|
|
|
# $id: id to cancel, can also be queue entry |
425
|
|
|
|
|
|
|
# Returns: bool, true if the was something canceled |
426
|
|
|
|
|
|
|
########################################################################### |
427
|
|
|
|
|
|
|
sub cancel_delivery { |
428
|
341
|
|
|
341
|
1
|
795
|
my Net::SIP::Dispatcher $self = shift; |
429
|
341
|
|
|
|
|
676
|
my ($callid,$id,$qentry); |
430
|
341
|
100
|
|
|
|
1027
|
if ( @_ == 2 ) { |
431
|
48
|
|
|
|
|
322
|
my $typ = shift; |
432
|
48
|
50
|
|
|
|
183
|
if ( $typ eq 'callid' ) { $callid = shift } |
|
48
|
0
|
|
|
|
127
|
|
|
|
0
|
|
|
|
|
|
433
|
0
|
|
|
|
|
0
|
elsif ( $typ eq 'id' ) { $id = shift } |
434
|
0
|
|
|
|
|
0
|
elsif ( $typ eq 'qentry' ) { $qentry = shift } |
435
|
|
|
|
|
|
|
else { |
436
|
0
|
|
|
|
|
0
|
croak( "bad typ '$typ', should be id|callid|qentry" ); |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
} else { |
439
|
293
|
|
|
|
|
525
|
$id = shift; |
440
|
293
|
100
|
|
|
|
776
|
if ( ref($id)) { |
441
|
133
|
|
|
|
|
267
|
$qentry = $id; |
442
|
133
|
|
|
|
|
286
|
$id = undef; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
} |
445
|
341
|
|
|
|
|
621
|
my $q = $self->{queue}; |
446
|
341
|
|
|
|
|
634
|
my $qn = @$q; |
447
|
341
|
100
|
|
|
|
1492
|
if ( $qentry ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# it's a *::Dispatcher::Packet |
449
|
133
|
|
|
|
|
698
|
DEBUG( 100,"cancel packet id: $qentry->{id}" ); |
450
|
133
|
|
|
|
|
408
|
@$q = grep { $_ != $qentry } @$q; |
|
136
|
|
|
|
|
510
|
|
451
|
|
|
|
|
|
|
} elsif ( defined $id ) { |
452
|
43
|
|
|
43
|
|
324
|
no warnings; # $_->{id} can be undef |
|
43
|
|
|
|
|
103
|
|
|
43
|
|
|
|
|
3047
|
|
453
|
160
|
|
|
|
|
1224
|
DEBUG( 100, "cancel packet id $id" ); |
454
|
160
|
|
|
|
|
417
|
@$q = grep { $_->{id} ne $id } @$q; |
|
59
|
|
|
|
|
752
|
|
455
|
|
|
|
|
|
|
} elsif ( defined $callid ) { |
456
|
43
|
|
|
43
|
|
249
|
no warnings; # $_->{callid} can be undef |
|
43
|
|
|
|
|
101
|
|
|
43
|
|
|
|
|
140737
|
|
457
|
48
|
|
|
|
|
206
|
DEBUG( 100, "cancel packet callid $callid" ); |
458
|
48
|
|
|
|
|
119
|
@$q = grep { $_->{callid} ne $callid } @$q; |
|
4
|
|
|
|
|
23
|
|
459
|
|
|
|
|
|
|
} else { |
460
|
0
|
|
|
|
|
0
|
croak( "cancel_delivery w/o id" ); |
461
|
|
|
|
|
|
|
} |
462
|
341
|
|
|
|
|
2747
|
return @$q < $qn; # true if items got deleted |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
########################################################################### |
468
|
|
|
|
|
|
|
# Receive a packet from a leg and forward it to the upper layer |
469
|
|
|
|
|
|
|
# if the packet is a request and I have a cached response resend it |
470
|
|
|
|
|
|
|
# w/o involving the upper layer |
471
|
|
|
|
|
|
|
# Args: ($self,$packet,$leg,$from) |
472
|
|
|
|
|
|
|
# $packet: Net::SIP::Packet |
473
|
|
|
|
|
|
|
# $leg: through which leg it was received |
474
|
|
|
|
|
|
|
# $from: where the packet comes from: [proto,ip,from,family] |
475
|
|
|
|
|
|
|
# Returns: NONE |
476
|
|
|
|
|
|
|
# Comment: if no receiver is defined using set_receiver the packet |
477
|
|
|
|
|
|
|
# will be silently dropped |
478
|
|
|
|
|
|
|
########################################################################### |
479
|
|
|
|
|
|
|
sub receive { |
480
|
216
|
|
|
216
|
1
|
436
|
my Net::SIP::Dispatcher $self = shift; |
481
|
216
|
|
|
|
|
494
|
my ($packet,$leg,$from) = @_; |
482
|
|
|
|
|
|
|
|
483
|
216
|
100
|
|
|
|
592
|
if ( $packet->is_request ) { |
484
|
77
|
|
|
|
|
181
|
my $cache = $self->{response_cache}; |
485
|
77
|
100
|
|
|
|
258
|
if ( %$cache ) { |
486
|
|
|
|
|
|
|
my $cid = join( "\0", |
487
|
47
|
|
|
|
|
138
|
map { $packet->get_header($_) } |
|
188
|
|
|
|
|
430
|
|
488
|
|
|
|
|
|
|
qw( cseq call-id from to ) |
489
|
|
|
|
|
|
|
); |
490
|
|
|
|
|
|
|
|
491
|
47
|
100
|
|
|
|
221
|
if ( my $response = $cache->{$cid} ) { |
492
|
|
|
|
|
|
|
# I have a cached response, use it |
493
|
|
|
|
|
|
|
$self->deliver($response->{packet}, |
494
|
1
|
|
|
|
|
6
|
leg => $leg, |
495
|
|
|
|
|
|
|
dst_addr => $from, |
496
|
|
|
|
|
|
|
); |
497
|
1
|
|
|
|
|
3
|
return; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
215
|
|
|
|
|
1027
|
invoke_callback( $self->{receiver},$packet,$leg,$from ); |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
########################################################################### |
506
|
|
|
|
|
|
|
# expire the entries on the queue, eg removes expired entries and |
507
|
|
|
|
|
|
|
# calls callback if necessary |
508
|
|
|
|
|
|
|
# expires also the response cache |
509
|
|
|
|
|
|
|
# Args: ($self;$time) |
510
|
|
|
|
|
|
|
# $time: expire regarding $time, if not given use time() |
511
|
|
|
|
|
|
|
# Returns: undef|$min_expire |
512
|
|
|
|
|
|
|
# $min_expire: time when next thing expires (undef if nothing to expire) |
513
|
|
|
|
|
|
|
########################################################################### |
514
|
|
|
|
|
|
|
sub queue_expire { |
515
|
174
|
|
|
174
|
1
|
353
|
my Net::SIP::Dispatcher $self = shift; |
516
|
174
|
|
33
|
|
|
1323
|
my $now = shift || $self->{eventloop}->looptime; |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# expire queue |
519
|
174
|
|
|
|
|
1059
|
my $queue = $self->{queue}; |
520
|
174
|
|
|
|
|
396
|
my (@nq,$changed,$min_expire); |
521
|
174
|
|
|
|
|
1962
|
foreach my $qe (@$queue) { |
522
|
|
|
|
|
|
|
|
523
|
10
|
|
|
|
|
22
|
my $retransmit; |
524
|
10
|
50
|
|
|
|
38
|
if ( my $retransmits = $qe->{retransmits} ) { |
525
|
10
|
|
66
|
|
|
88
|
while ( @$retransmits && $retransmits->[0] < $now ) { |
526
|
8
|
|
|
|
|
31
|
$retransmit = shift(@$retransmits); |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
10
|
50
|
|
|
|
38
|
if ( !@$retransmits ) { |
530
|
|
|
|
|
|
|
# completely expired |
531
|
0
|
|
|
|
|
0
|
DEBUG( 50,"entry %s expired because expire=%.2f but now=%d", $qe->tid,$retransmit,$now ); |
532
|
0
|
|
|
|
|
0
|
$changed++; |
533
|
0
|
|
|
|
|
0
|
$qe->trigger_callback( ETIMEDOUT ); |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# don't put into new queue |
536
|
0
|
|
|
|
|
0
|
next; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
10
|
100
|
|
|
|
26
|
if ( $retransmit ) { |
540
|
|
|
|
|
|
|
# need to retransmit the packet |
541
|
5
|
|
|
|
|
23
|
$self->__deliver( $qe ); |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
10
|
|
|
|
|
27
|
my $next_retransmit = $retransmits->[0]; |
545
|
10
|
100
|
66
|
|
|
51
|
if ( !defined($min_expire) || $next_retransmit<$min_expire ) { |
546
|
9
|
|
|
|
|
21
|
$min_expire = $next_retransmit |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
10
|
|
|
|
|
27
|
push @nq,$qe; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
} |
552
|
174
|
50
|
|
|
|
720
|
$self->{queue} = \@nq if $changed; |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# expire response cache |
555
|
174
|
|
|
|
|
455
|
my $cache = $self->{response_cache}; |
556
|
174
|
|
|
|
|
877
|
foreach my $cid ( keys %$cache ) { |
557
|
83
|
|
|
|
|
251
|
my $expire = $cache->{$cid}{expire}; |
558
|
83
|
50
|
100
|
|
|
792
|
if ( $expire < $now ) { |
|
|
100
|
|
|
|
|
|
559
|
0
|
|
|
|
|
0
|
delete $cache->{$cid}; |
560
|
|
|
|
|
|
|
} elsif ( !defined($min_expire) || $expire<$min_expire ) { |
561
|
68
|
|
|
|
|
208
|
$min_expire = $expire |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# return time to next expire for optimizations |
566
|
174
|
|
|
|
|
974
|
return $min_expire; |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
########################################################################### |
571
|
|
|
|
|
|
|
# the real delivery of a queue entry: |
572
|
|
|
|
|
|
|
# if no leg,addr try to determine them from request-URI |
573
|
|
|
|
|
|
|
# prepare timeout handling |
574
|
|
|
|
|
|
|
# Args: ($self,$qentry) |
575
|
|
|
|
|
|
|
# $qentry: Net::SIP::Dispatcher::Packet |
576
|
|
|
|
|
|
|
# Returns: NONE |
577
|
|
|
|
|
|
|
# Comment: |
578
|
|
|
|
|
|
|
# this might be called several times for a queue entry, eg as a callback |
579
|
|
|
|
|
|
|
# at the various stages (find leg,addr for URI needs DNS lookup which |
580
|
|
|
|
|
|
|
# might be done asynchronous, eg callback driven, send might be callback |
581
|
|
|
|
|
|
|
# driven for tcp connections which need connect, multiple writes...) |
582
|
|
|
|
|
|
|
########################################################################### |
583
|
|
|
|
|
|
|
sub __deliver { |
584
|
314
|
|
|
314
|
|
566
|
my Net::SIP::Dispatcher $self = shift; |
585
|
314
|
|
|
|
|
450
|
my $qentry = shift; |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
# loop until leg und dst_addr are known, when we call leg->deliver |
588
|
314
|
|
|
|
|
590
|
my $leg = $qentry->{leg}[0]; |
589
|
314
|
50
|
66
|
|
|
1259
|
if ( $leg && @{ $qentry->{leg}}>1 ) { |
|
240
|
|
|
|
|
923
|
|
590
|
0
|
|
|
|
|
0
|
DEBUG( 50,"picking first of multiple legs: ".join( " ", map { $_->dump } @{ $qentry->{leg}} )); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
591
|
|
|
|
|
|
|
} |
592
|
314
|
|
|
|
|
629
|
my $dst_addr = $qentry->{dst_addr}[0]; |
593
|
|
|
|
|
|
|
|
594
|
314
|
100
|
66
|
|
|
1506
|
if ( ! $dst_addr || ! $leg) { |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
# if explicit routes given use first route |
597
|
|
|
|
|
|
|
# else resolve URI from request |
598
|
|
|
|
|
|
|
|
599
|
113
|
|
|
|
|
196
|
my $uri; |
600
|
113
|
|
|
|
|
226
|
my $packet = $qentry->{packet}; |
601
|
113
|
50
|
|
|
|
380
|
if ( my ($route) = $packet->get_header( 'route' )) { |
602
|
0
|
|
|
|
|
0
|
($uri) = sip_hdrval2parts( route => $route ); |
603
|
|
|
|
|
|
|
} else { |
604
|
113
|
|
|
|
|
534
|
$uri = $packet->uri; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
113
|
|
|
|
|
668
|
DEBUG( 100,"no dst_addr or leg yet, uri='$uri'" ); |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
my $callback = sub { |
610
|
113
|
|
|
113
|
|
246
|
my ($self,$qentry,@error) = @_; |
611
|
113
|
50
|
|
|
|
259
|
if ( @error ) { |
612
|
0
|
|
|
|
|
0
|
$qentry->trigger_callback(@error); |
613
|
0
|
|
|
|
|
0
|
return $self->cancel_delivery( $qentry ); |
614
|
|
|
|
|
|
|
} else { |
615
|
113
|
|
|
|
|
575
|
$self->__deliver($qentry); |
616
|
|
|
|
|
|
|
} |
617
|
113
|
|
|
|
|
1012
|
}; |
618
|
|
|
|
|
|
|
return $self->resolve_uri( |
619
|
|
|
|
|
|
|
$uri, |
620
|
|
|
|
|
|
|
$qentry->{dst_addr}, |
621
|
|
|
|
|
|
|
$qentry->{leg}, |
622
|
|
|
|
|
|
|
[ $callback, $self,$qentry ], |
623
|
|
|
|
|
|
|
$qentry->{proto}, |
624
|
113
|
|
|
|
|
968
|
); |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
201
|
100
|
100
|
|
|
1044
|
if ($qentry->{retransmits} && ! $leg->do_retransmits) { |
628
|
30
|
|
|
|
|
165
|
$qentry->{retransmits} = undef; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
# I have leg and addr, send packet thru leg to addr |
632
|
|
|
|
|
|
|
my $cb = sub { |
633
|
88
|
|
|
88
|
|
253
|
my ($self,$qentry,$error) = @_; |
634
|
88
|
50
|
|
|
|
231
|
$self || return; |
635
|
88
|
50
|
33
|
|
|
593
|
if ( !$error && $qentry->{retransmits} ) { |
636
|
|
|
|
|
|
|
# remove from queue even if timeout |
637
|
0
|
|
|
|
|
0
|
$self->cancel_delivery( $qentry ); |
638
|
|
|
|
|
|
|
} |
639
|
88
|
|
|
|
|
418
|
$qentry->trigger_callback( $error ); |
640
|
201
|
|
|
|
|
1605
|
}; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# adds via on cloned packet, calls cb if definite success (tcp) |
643
|
|
|
|
|
|
|
# or error |
644
|
|
|
|
|
|
|
#Carp::confess("expected reference, got $dst_addr") if !ref($dst_addr); |
645
|
201
|
50
|
|
|
|
704
|
$DEBUG && DEBUG(50,"deliver through leg ".$leg->dump.' @' |
646
|
|
|
|
|
|
|
.ip_parts2string($dst_addr)); |
647
|
201
|
|
|
|
|
932
|
weaken( my $rself = \$self ); |
648
|
201
|
|
|
|
|
523
|
$cb = [ $cb,$self,$qentry ]; |
649
|
201
|
|
|
|
|
662
|
weaken( $cb->[1] ); |
650
|
201
|
|
|
|
|
1559
|
$leg->deliver( $qentry->{packet},$dst_addr,$cb ); |
651
|
|
|
|
|
|
|
|
652
|
201
|
100
|
|
|
|
3890
|
if ( !$qentry->{retransmits} ) { |
653
|
|
|
|
|
|
|
# remove from queue if no timeout |
654
|
133
|
|
|
|
|
731
|
$self->cancel_delivery( $qentry ); |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
########################################################################### |
661
|
|
|
|
|
|
|
# resolve URI, determine dst_addr and outgoing leg |
662
|
|
|
|
|
|
|
# Args: ($self,$uri,$dst_addr,$legs,$callback;$allowed_proto,$allowed_legs) |
663
|
|
|
|
|
|
|
# $uri: URI to resolve |
664
|
|
|
|
|
|
|
# $dst_addr: reference to list where to put dst_addr |
665
|
|
|
|
|
|
|
# $legs: reference to list where to put leg |
666
|
|
|
|
|
|
|
# $callback: called with () if resolved successfully, else called |
667
|
|
|
|
|
|
|
# with @error |
668
|
|
|
|
|
|
|
# $allowed_proto: optional \@list of protocols (default udp, tcp, tls). |
669
|
|
|
|
|
|
|
# If given only only these protocols will be considered and in this order. |
670
|
|
|
|
|
|
|
# $allowed_legs: optional list of legs which are allowed |
671
|
|
|
|
|
|
|
# Returns: NONE |
672
|
|
|
|
|
|
|
########################################################################### |
673
|
|
|
|
|
|
|
sub resolve_uri { |
674
|
120
|
|
|
120
|
1
|
260
|
my Net::SIP::Dispatcher $self = shift; |
675
|
120
|
|
|
|
|
574
|
my ($uri,$dst_addr,$legs,$callback,$allowed_proto,$allowed_legs) = @_; |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
# packet should be a request packet (see constructor of *::Dispatcher::Packet) |
678
|
120
|
|
|
|
|
518
|
my ($domain,$user,$sip_proto,$param) = sip_uri2parts($uri); |
679
|
120
|
50
|
|
|
|
3120
|
$domain or do { |
680
|
0
|
|
|
|
|
0
|
DEBUG( 50,"bad URI '$uri'" ); |
681
|
0
|
|
|
|
|
0
|
return invoke_callback($callback, EHOSTUNREACH ); |
682
|
|
|
|
|
|
|
}; |
683
|
|
|
|
|
|
|
|
684
|
120
|
|
|
|
|
595
|
my @proto; |
685
|
120
|
|
|
|
|
223
|
my $default_port = 5060; |
686
|
120
|
100
|
|
|
|
583
|
if ( $sip_proto eq 'sips' ) { |
|
|
100
|
|
|
|
|
|
687
|
10
|
|
|
|
|
34
|
$default_port = 5061; |
688
|
10
|
|
|
|
|
60
|
@proto = 'tls'; |
689
|
|
|
|
|
|
|
} elsif ( my $p = $param->{transport} ) { |
690
|
|
|
|
|
|
|
# explicit spec of proto |
691
|
4
|
|
|
|
|
34
|
@proto = lc($p) |
692
|
|
|
|
|
|
|
} else { |
693
|
|
|
|
|
|
|
# XXXX maybe we should use tcp first if the packet has a specific |
694
|
|
|
|
|
|
|
# minimum length, udp should not be used at all if the packet size is > 2**16 |
695
|
106
|
|
|
|
|
737
|
@proto = ( 'udp','tcp' ); |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# change @proto so that only the protocols from $allowed_proto are ini it |
699
|
|
|
|
|
|
|
# and that they are tried in the order from $allowed_proto |
700
|
120
|
50
|
33
|
|
|
449
|
if ( $allowed_proto && @$allowed_proto ) { |
701
|
0
|
|
|
|
|
0
|
my @proto_new; |
702
|
0
|
|
|
|
|
0
|
foreach my $ap ( @$allowed_proto ) { |
703
|
0
|
|
|
0
|
|
0
|
my $p = first { $ap eq $_ } @proto; |
|
0
|
|
|
|
|
0
|
|
704
|
0
|
0
|
|
|
|
0
|
push @proto_new,$p if $p; |
705
|
|
|
|
|
|
|
} |
706
|
0
|
|
|
|
|
0
|
@proto = @proto_new; |
707
|
0
|
0
|
|
|
|
0
|
@proto or do { |
708
|
0
|
|
|
|
|
0
|
DEBUG( 50,"no protocols allowed for $uri" ); |
709
|
0
|
|
|
|
|
0
|
@$dst_addr = (); |
710
|
0
|
|
|
|
|
0
|
return invoke_callback( $callback, ENOPROTOOPT ); # no proto available |
711
|
|
|
|
|
|
|
}; |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
120
|
|
50
|
|
|
335
|
$dst_addr ||= []; |
715
|
120
|
|
50
|
|
|
977
|
$allowed_legs ||= [ $self->get_legs ]; |
716
|
120
|
100
|
|
|
|
328
|
if ( @$legs ) { |
717
|
39
|
|
|
|
|
107
|
my %allowed = map { $_ => 1 } @$legs; |
|
39
|
|
|
|
|
214
|
|
718
|
39
|
|
|
|
|
112
|
@$allowed_legs = grep { $allowed{$_} } @$allowed_legs; |
|
39
|
|
|
|
|
200
|
|
719
|
|
|
|
|
|
|
} |
720
|
120
|
50
|
|
|
|
332
|
@$allowed_legs or do { |
721
|
0
|
|
|
|
|
0
|
DEBUG( 50,"no legs allowed for '$uri'" ); |
722
|
0
|
|
|
|
|
0
|
return invoke_callback($callback, EHOSTUNREACH ); |
723
|
|
|
|
|
|
|
}; |
724
|
|
|
|
|
|
|
|
725
|
120
|
|
|
|
|
215
|
my $ip_addr = $param->{maddr}; |
726
|
|
|
|
|
|
|
{ |
727
|
120
|
100
|
|
|
|
185
|
my ($host,$port,$family) = ip_string2parts($domain, $ip_addr ? 1:0); |
|
120
|
|
|
|
|
593
|
|
728
|
120
|
100
|
|
|
|
370
|
$default_port = $port if defined $port; |
729
|
120
|
100
|
|
|
|
295
|
if ($family) { |
730
|
68
|
|
33
|
|
|
2668
|
$ip_addr ||= $host; |
731
|
68
|
|
|
|
|
263
|
$domain = ip_ptr($host,$family); |
732
|
|
|
|
|
|
|
} else { |
733
|
52
|
|
|
|
|
209
|
$domain = $host; |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
} |
736
|
120
|
|
|
|
|
612
|
DEBUG( 100,"domain=$domain" ); |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# do we have a fixed proxy for the domain or upper domain? |
739
|
120
|
50
|
|
|
|
474
|
if ( ! @$dst_addr ) { |
740
|
120
|
|
|
|
|
289
|
my $d2p = $self->{domain2proxy}; |
741
|
120
|
100
|
66
|
|
|
1109
|
if ( $d2p && %$d2p ) { |
742
|
81
|
|
|
|
|
806
|
my $dom = $domain; |
743
|
81
|
|
|
|
|
162
|
my $addr = $d2p->{$dom}; # exact match |
744
|
81
|
|
|
|
|
240
|
while ( ! $addr) { |
745
|
221
|
100
|
|
|
|
763
|
$dom =~s{^[^\.]+\.}{} or last; |
746
|
183
|
|
|
|
|
403
|
$addr = $d2p->{ "*.$dom" }; |
747
|
|
|
|
|
|
|
} |
748
|
81
|
|
100
|
|
|
310
|
$addr ||= $d2p->{ $dom = '*'}; # catch-all |
749
|
81
|
100
|
|
|
|
192
|
if ( $addr ) { |
750
|
45
|
|
|
|
|
231
|
DEBUG( 50,"setting dst_addr from domain specific proxy for domain $dom" ); |
751
|
45
|
|
|
|
|
126
|
@$dst_addr = @$addr; |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# do we have a global outgoing proxy? |
757
|
120
|
50
|
66
|
|
|
939
|
if ( !@$dst_addr |
758
|
|
|
|
|
|
|
&& ( my $addr = $self->{outgoing_proxy} )) { |
759
|
|
|
|
|
|
|
# if we have a fixed outgoing proxy use it |
760
|
0
|
|
|
|
|
0
|
DEBUG( 50,"setting dst_addr+leg to $addr from outgoing_proxy" ); |
761
|
0
|
|
|
|
|
0
|
@$dst_addr = ( $addr ); |
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# is it an IP address? |
765
|
120
|
100
|
66
|
|
|
560
|
if ( !@$dst_addr && $ip_addr ) { |
766
|
75
|
|
|
|
|
245
|
DEBUG( 50,"setting dst_addr from URI because IP address given" ); |
767
|
75
|
|
|
|
|
169
|
@$dst_addr = ( $ip_addr ); |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
# is param maddr set? |
771
|
120
|
100
|
|
|
|
362
|
if ( my $ip = $param->{maddr} ) { |
772
|
7
|
50
|
|
|
|
130
|
@$dst_addr = ($ip) if ip_is_v46($ip); |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# entries are hashes of prio,proto,host,addr,port,family |
777
|
120
|
|
|
|
|
209
|
my @resp; |
778
|
120
|
|
|
|
|
524
|
foreach my $addr ( @$dst_addr ) { |
779
|
147
|
100
|
|
|
|
394
|
if ( ref($addr)) { |
780
|
72
|
|
|
|
|
162
|
push @resp,$addr; # right format: see domain2proxy |
781
|
|
|
|
|
|
|
} else { |
782
|
75
|
50
|
|
|
|
301
|
my ($proto,$host,$port,$family) = sip_uri2sockinfo($addr) |
783
|
|
|
|
|
|
|
or next; |
784
|
75
|
50
|
33
|
|
|
1120
|
$addr = lock_ref_keys({ |
785
|
|
|
|
|
|
|
proto => $proto, |
786
|
|
|
|
|
|
|
host => $host, |
787
|
|
|
|
|
|
|
addr => $family ? $host : undef, |
788
|
|
|
|
|
|
|
port => $port || $default_port, |
789
|
|
|
|
|
|
|
family => $family |
790
|
|
|
|
|
|
|
}); |
791
|
75
|
50
|
|
|
|
846
|
push @resp, map { lock_ref_keys({ |
|
139
|
|
|
|
|
1665
|
|
792
|
|
|
|
|
|
|
%$addr, |
793
|
|
|
|
|
|
|
proto => $_, |
794
|
|
|
|
|
|
|
prio => SRV_PRIO_UNDEF, |
795
|
|
|
|
|
|
|
}) } $proto ? ($proto) : @proto; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
# should we use a fixed transport? |
800
|
120
|
100
|
66
|
|
|
1771
|
if (@resp and my $proto = $param->{transport} ) { |
801
|
4
|
|
|
|
|
34
|
$proto = lc($proto); |
802
|
4
|
50
|
|
|
|
80
|
if ($proto eq 'udp') { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
803
|
0
|
|
|
|
|
0
|
@resp = grep { $_->{proto} eq 'udp' } @resp |
|
0
|
|
|
|
|
0
|
|
804
|
|
|
|
|
|
|
} elsif ($proto eq 'tcp') { |
805
|
|
|
|
|
|
|
# accept proto tcp and tls |
806
|
4
|
|
|
|
|
34
|
@resp = grep { $_->{proto} ne 'udp' } @resp |
|
4
|
|
|
|
|
44
|
|
807
|
|
|
|
|
|
|
} elsif ($proto eq 'tls') { |
808
|
0
|
|
|
|
|
0
|
@resp = grep { $_->{proto} eq 'tls' } @resp |
|
0
|
|
|
|
|
0
|
|
809
|
|
|
|
|
|
|
} else { |
810
|
|
|
|
|
|
|
# no matching proto available |
811
|
0
|
|
|
|
|
0
|
@resp = (); |
812
|
|
|
|
|
|
|
} |
813
|
4
|
50
|
|
|
|
44
|
return invoke_callback($callback, ENOPROTOOPT) if ! @resp; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
120
|
|
|
|
|
452
|
my @param = ( $dst_addr,$legs,$allowed_legs,$default_port,$callback ); |
817
|
120
|
50
|
|
|
|
378
|
if (@resp) { |
818
|
|
|
|
|
|
|
# directly call __resolve_uri_final if all names are resolved |
819
|
|
|
|
|
|
|
return __resolve_uri_final( @param,\@resp ) |
820
|
120
|
50
|
|
|
|
240
|
if ! grep { ! $_->{addr} } @resp; |
|
211
|
|
|
|
|
2566
|
|
821
|
0
|
|
|
|
|
0
|
return $self->dns_host2ip(\@resp, |
822
|
|
|
|
|
|
|
[ \&__resolve_uri_final, @param ]); |
823
|
|
|
|
|
|
|
} |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
# If no fixed mapping DNS needs to be used |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
# XXXX no full support for RFC3263, eg we don't support NAPTR |
828
|
|
|
|
|
|
|
# but query instead directly for _sip._udp.domain.. like in |
829
|
|
|
|
|
|
|
# RFC2543 specified |
830
|
|
|
|
|
|
|
|
831
|
0
|
|
|
|
|
0
|
return $self->dns_domain2srv($domain, \@proto, |
832
|
|
|
|
|
|
|
[ \&__resolve_uri_final, @param ]); |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
sub __resolve_uri_final { |
836
|
120
|
|
|
120
|
|
408
|
my ($dst_addr,$legs,$allowed_legs,$default_port,$callback,$resp) = @_; |
837
|
120
|
50
|
|
|
|
355
|
$DEBUG && DEBUG_DUMP( 100,$resp ); |
838
|
|
|
|
|
|
|
|
839
|
120
|
50
|
33
|
|
|
1056
|
return invoke_callback( $callback,EHOSTUNREACH ) |
840
|
|
|
|
|
|
|
unless $resp && @$resp; |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# for A|AAAA records we got no port, use default_port |
843
|
120
|
|
33
|
|
|
624
|
$_->{port} ||= $default_port for(@$resp); |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
# sort by prio |
846
|
|
|
|
|
|
|
# FIXME: can contradict order in @proto |
847
|
120
|
|
|
|
|
643
|
@$resp = sort { $a->{prio} <=> $b->{prio} } @$resp; |
|
91
|
|
|
|
|
1634
|
|
848
|
|
|
|
|
|
|
|
849
|
120
|
|
|
|
|
368
|
@$dst_addr = (); |
850
|
120
|
|
|
|
|
210
|
@$legs = (); |
851
|
120
|
|
|
|
|
332
|
foreach my $r ( @$resp ) { |
852
|
|
|
|
|
|
|
my $leg = first { $_->can_deliver_to( |
853
|
|
|
|
|
|
|
proto => $r->{proto}, |
854
|
|
|
|
|
|
|
host => $r->{host}, |
855
|
|
|
|
|
|
|
addr => $r->{addr}, |
856
|
|
|
|
|
|
|
port => $r->{port}, |
857
|
|
|
|
|
|
|
family => $r->{family}, |
858
|
211
|
|
|
223
|
|
1790
|
)} @$allowed_legs; |
|
223
|
|
|
|
|
1559
|
|
859
|
|
|
|
|
|
|
|
860
|
211
|
100
|
|
|
|
3439
|
if ( $leg ) { |
861
|
126
|
|
|
|
|
238
|
push @$dst_addr, $r; |
862
|
126
|
|
|
|
|
307
|
push @$legs,$leg; |
863
|
|
|
|
|
|
|
} else { |
864
|
85
|
|
|
|
|
534
|
DEBUG(50,"no leg with $r->{proto} to %s", ip_parts2string($r)); |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
|
868
|
120
|
50
|
|
|
|
355
|
return invoke_callback( $callback, EHOSTUNREACH ) if !@$dst_addr; |
869
|
120
|
|
|
|
|
482
|
invoke_callback( $callback ); |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
sub _find_leg4addr { |
874
|
0
|
|
|
0
|
|
0
|
my Net::SIP::Dispatcher $self = shift; |
875
|
0
|
|
|
|
|
0
|
my $dst_addr = shift; |
876
|
0
|
0
|
|
|
|
0
|
if (!ref($dst_addr)) { |
877
|
0
|
|
|
|
|
0
|
my @si = sip_uri2sockinfo($dst_addr); |
878
|
0
|
0
|
|
|
|
0
|
$dst_addr = lock_ref_keys({ |
879
|
|
|
|
|
|
|
proto => $si[0], |
880
|
|
|
|
|
|
|
host => $si[1], |
881
|
|
|
|
|
|
|
addr => $si[3] ? $si[1] : undef, |
882
|
|
|
|
|
|
|
port => $si[2], |
883
|
|
|
|
|
|
|
family => $si[3], |
884
|
|
|
|
|
|
|
}); |
885
|
|
|
|
|
|
|
} |
886
|
0
|
|
|
|
|
0
|
return grep { $_->can_deliver_to(%$dst_addr) } @{ $self->{legs} }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
########################################################################### |
890
|
|
|
|
|
|
|
# resolve hostname to IP using DNS |
891
|
|
|
|
|
|
|
# Args: ($self,$host,$callback) |
892
|
|
|
|
|
|
|
# $host: hostname or hash with hostname as keys or list of hashes which have |
893
|
|
|
|
|
|
|
# a host value but miss an addr value |
894
|
|
|
|
|
|
|
# $callback: gets called with (result)|() once finished |
895
|
|
|
|
|
|
|
# result is @IP for single hosts or the input hash ref where the |
896
|
|
|
|
|
|
|
# IPs are filled in as values or the list filled with addr, family |
897
|
|
|
|
|
|
|
# Returns: NONE |
898
|
|
|
|
|
|
|
########################################################################### |
899
|
|
|
|
|
|
|
sub dns_host2ip { |
900
|
0
|
|
|
0
|
0
|
0
|
my Net::SIP::Dispatcher $self = shift; |
901
|
0
|
|
|
|
|
0
|
my ($host,$callback) = @_; |
902
|
|
|
|
|
|
|
|
903
|
0
|
|
|
|
|
0
|
my (@rec,$cb); |
904
|
0
|
0
|
|
|
|
0
|
if (!ref($host)) { |
|
|
0
|
|
|
|
|
|
905
|
|
|
|
|
|
|
# scalar: return ip(s) |
906
|
0
|
|
|
|
|
0
|
@rec = { host => $host }; |
907
|
|
|
|
|
|
|
my $transform = sub { |
908
|
0
|
|
|
0
|
|
0
|
my ($callback,$res) = @_; |
909
|
|
|
|
|
|
|
invoke_callback($callback, |
910
|
0
|
|
|
|
|
0
|
grep { $_ } map { $_->{addr} } @$res); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
911
|
0
|
|
|
|
|
0
|
}; |
912
|
0
|
|
|
|
|
0
|
$cb = [ $transform, $callback ]; |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
} elsif (ref($host) eq 'HASH') { |
915
|
|
|
|
|
|
|
# hash: fill hash values |
916
|
0
|
|
|
|
|
0
|
@rec = map { (host => $_) } keys(%$host); |
|
0
|
|
|
|
|
0
|
|
917
|
0
|
0
|
|
|
|
0
|
return invoke_callback($callback, $host) if ! @rec; |
918
|
|
|
|
|
|
|
my $transform = sub { |
919
|
0
|
|
|
0
|
|
0
|
my ($host,$callback,$res) = @_; |
920
|
0
|
|
|
|
|
0
|
$host->{$_->{host}} = $_->{addr} for @$res; |
921
|
0
|
|
|
|
|
0
|
invoke_callback($callback, $host); |
922
|
0
|
|
|
|
|
0
|
}; |
923
|
0
|
|
|
|
|
0
|
$cb = [ $transform, $host, $callback ]; |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
} else { |
926
|
|
|
|
|
|
|
# list of hashes: fill in addr and family in place |
927
|
0
|
|
|
|
|
0
|
my @hasip; |
928
|
0
|
|
|
|
|
0
|
for(@$host) { |
929
|
0
|
0
|
|
|
|
0
|
if ($_->{addr}) { |
930
|
0
|
|
|
|
|
0
|
push @hasip, $_; |
931
|
|
|
|
|
|
|
} else { |
932
|
0
|
|
|
|
|
0
|
push @rec, $_; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
} |
935
|
0
|
0
|
|
|
|
0
|
return invoke_callback($callback, $host) if ! @rec; |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
my $transform = sub { |
938
|
0
|
|
|
0
|
|
0
|
my ($hasip,$callback,$res) = @_; |
939
|
|
|
|
|
|
|
# original order might be changed !!! |
940
|
0
|
|
|
|
|
0
|
push @$res, @$hasip; |
941
|
0
|
|
|
|
|
0
|
invoke_callback($callback, $res); |
942
|
0
|
|
|
|
|
0
|
}; |
943
|
0
|
|
|
|
|
0
|
$cb = [ $transform, \@hasip, $callback ]; |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
0
|
my @queries; |
947
|
0
|
|
|
|
|
0
|
for (@rec) { |
948
|
0
|
|
|
|
|
0
|
my %q = (name => $_->{host}, rec => $_); |
949
|
0
|
|
|
|
|
0
|
push @queries, { type => 'AAAA', %q } if CAN_IPV6; |
950
|
0
|
|
|
|
|
0
|
push @queries, { type => 'A', %q }; |
951
|
|
|
|
|
|
|
} |
952
|
|
|
|
|
|
|
|
953
|
0
|
|
0
|
|
|
0
|
my $res = $self->{dnsresolv} || __net_dns_resolver($self->{eventloop}); |
954
|
0
|
|
|
|
|
0
|
__generic_resolver({ |
955
|
|
|
|
|
|
|
queries => \@queries, |
956
|
|
|
|
|
|
|
callback => $cb, |
957
|
|
|
|
|
|
|
resolver => $res, |
958
|
|
|
|
|
|
|
}); |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
########################################################################### |
962
|
|
|
|
|
|
|
# get SRV records using DNS |
963
|
|
|
|
|
|
|
# Args: ($self,$domain,$proto,$sip_proto,$callback) |
964
|
|
|
|
|
|
|
# $domain: domain for SRV query |
965
|
|
|
|
|
|
|
# $proto: which protocols to check: list of udp|tcp|tls |
966
|
|
|
|
|
|
|
# $callback: gets called with result once finished |
967
|
|
|
|
|
|
|
# result is \@list of hashes with prio, proto, host ,port, family |
968
|
|
|
|
|
|
|
# Returns: NONE |
969
|
|
|
|
|
|
|
########################################################################### |
970
|
|
|
|
|
|
|
sub dns_domain2srv { |
971
|
0
|
|
|
0
|
0
|
0
|
my Net::SIP::Dispatcher $self = shift; |
972
|
0
|
|
|
|
|
0
|
my ($domain,$protos,$callback) = @_; |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# Try to get SRV records for _sip._udp.domain or _sip._tcp.domain |
975
|
0
|
|
|
|
|
0
|
my @queries; |
976
|
0
|
|
|
|
|
0
|
for(@$protos) { |
977
|
0
|
0
|
|
|
|
0
|
push @queries, { |
978
|
|
|
|
|
|
|
type => 'SRV', |
979
|
|
|
|
|
|
|
name => $_ eq 'tls' ? "_sips._tcp.$domain" : "_sip._$_.$domain", |
980
|
|
|
|
|
|
|
rec => { host => $domain, proto => $_ }, |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
} |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
# If we have any results for SRV we can break, |
985
|
|
|
|
|
|
|
# otherwise continue with with A|AAAA |
986
|
0
|
|
|
|
|
0
|
push @queries, { type => 'BREAK-IF-RESULTS' }; |
987
|
0
|
|
|
|
|
0
|
for(@$protos) { |
988
|
0
|
|
|
|
|
0
|
my %r = ( |
989
|
|
|
|
|
|
|
name => $domain, |
990
|
|
|
|
|
|
|
rec => { |
991
|
|
|
|
|
|
|
prio => SRV_PRIO_UNDEF, |
992
|
|
|
|
|
|
|
host => $domain, |
993
|
|
|
|
|
|
|
proto => $_, |
994
|
|
|
|
|
|
|
port => undef, |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
); |
997
|
0
|
|
|
|
|
0
|
push @queries, { type => 'AAAA', %r } if CAN_IPV6; |
998
|
0
|
|
|
|
|
0
|
push @queries, { type => 'A', %r }; |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
|
1001
|
0
|
|
0
|
|
|
0
|
my $res = $self->{dnsresolv} || __net_dns_resolver($self->{eventloop}); |
1002
|
0
|
|
|
|
|
0
|
__generic_resolver({ |
1003
|
|
|
|
|
|
|
queries => \@queries, |
1004
|
|
|
|
|
|
|
callback => $callback, |
1005
|
|
|
|
|
|
|
resolver => $res, |
1006
|
|
|
|
|
|
|
}); |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# generic internal resolver helper |
1011
|
|
|
|
|
|
|
# expects to be initially called as |
1012
|
|
|
|
|
|
|
# __generic_resolver({ |
1013
|
|
|
|
|
|
|
# queries => \@queries, |
1014
|
|
|
|
|
|
|
# callback => $callback, |
1015
|
|
|
|
|
|
|
# resolver => $res, |
1016
|
|
|
|
|
|
|
# }); |
1017
|
|
|
|
|
|
|
# |
1018
|
|
|
|
|
|
|
# where queries are a list of tasks for DNS lookup with |
1019
|
|
|
|
|
|
|
# type: SRV|A|AAAA |
1020
|
|
|
|
|
|
|
# name: the name to lookup |
1021
|
|
|
|
|
|
|
# rec: the record to enrich with |
1022
|
|
|
|
|
|
|
# SRV: prio, proto, host, addr, port, family |
1023
|
|
|
|
|
|
|
# A|AAAA: addr, family |
1024
|
|
|
|
|
|
|
# |
1025
|
|
|
|
|
|
|
# resolver is a function to do the actual resolving. |
1026
|
|
|
|
|
|
|
# An implementation using Net::DNS is done in __net_dns_resolver. |
1027
|
|
|
|
|
|
|
# It will be called as |
1028
|
|
|
|
|
|
|
# resolver->(type,name,callback) where |
1029
|
|
|
|
|
|
|
# type: SRV|A|AAAA |
1030
|
|
|
|
|
|
|
# name: the name to lookup |
1031
|
|
|
|
|
|
|
# callback: callback to invoke after lookup is done with the list of |
1032
|
|
|
|
|
|
|
# answers, i.e. list-ref containing |
1033
|
|
|
|
|
|
|
# [ 'SRV', prio, proto, host, port ] |
1034
|
|
|
|
|
|
|
# [ 'A', addr, name ] |
1035
|
|
|
|
|
|
|
# [ 'AAAA', addr, name ] |
1036
|
|
|
|
|
|
|
# |
1037
|
|
|
|
|
|
|
# callback is invoked when all queries are done with the list of |
1038
|
|
|
|
|
|
|
# enriched records |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
sub __generic_resolver { |
1041
|
0
|
|
|
0
|
|
0
|
my ($state,$qid,$ans) = @_; |
1042
|
0
|
0
|
0
|
|
|
0
|
$DEBUG && DEBUG_DUMP(100,[$qid,$ans]) if $qid; |
1043
|
|
|
|
|
|
|
|
1044
|
0
|
|
|
|
|
0
|
my $queries = $state->{queries}; |
1045
|
0
|
|
0
|
|
|
0
|
my $results = $state->{results} ||= []; |
1046
|
0
|
0
|
|
|
|
0
|
goto after_answers if !$qid; |
1047
|
|
|
|
|
|
|
|
1048
|
0
|
|
|
|
|
0
|
for(my $i=0; $i<@$queries; $i++) { |
1049
|
0
|
|
|
|
|
0
|
my $q = $queries->[$i]; |
1050
|
0
|
0
|
|
|
|
0
|
if ($q->{type} eq 'BREAK-IF-RESULTS') { |
1051
|
0
|
0
|
|
|
|
0
|
if (@$results) { |
1052
|
|
|
|
|
|
|
# skip remaining queries |
1053
|
0
|
|
|
|
|
0
|
@$queries = (); |
1054
|
0
|
|
|
|
|
0
|
last; |
1055
|
|
|
|
|
|
|
} |
1056
|
0
|
0
|
|
|
|
0
|
if ($i==0) { |
1057
|
|
|
|
|
|
|
# remove if top query |
1058
|
0
|
|
|
|
|
0
|
shift(@$queries); |
1059
|
0
|
|
|
|
|
0
|
$i--; |
1060
|
|
|
|
|
|
|
} |
1061
|
0
|
|
|
|
|
0
|
next; |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
|
1064
|
0
|
0
|
|
|
|
0
|
"$q->{type}:$q->{name}" eq $qid or next; |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
# query matches qid of answer, remove from @$queries |
1067
|
0
|
|
|
|
|
0
|
splice(@$queries,$i,1); |
1068
|
0
|
|
|
|
|
0
|
$i--; |
1069
|
|
|
|
|
|
|
|
1070
|
0
|
0
|
0
|
|
|
0
|
if ($q->{type} eq 'SRV') { |
|
|
0
|
|
|
|
|
|
1071
|
0
|
|
|
|
|
0
|
my (%addr2ip,@res); |
1072
|
0
|
|
|
|
|
0
|
for(@$ans) { |
1073
|
0
|
|
|
|
|
0
|
my $type = shift(@$_); |
1074
|
0
|
0
|
0
|
|
|
0
|
if ($type eq 'A' or CAN_IPV6 ? $type eq 'AAAA' : 0) { |
1075
|
|
|
|
|
|
|
# supplemental data |
1076
|
0
|
|
|
|
|
0
|
my ($ip,$name) = @_; |
1077
|
0
|
|
|
|
|
0
|
push @{ $addr2ip{$name}}, [$ip, $type]; |
|
0
|
|
|
|
|
0
|
|
1078
|
0
|
|
|
|
|
0
|
next; |
1079
|
|
|
|
|
|
|
} |
1080
|
0
|
0
|
|
|
|
0
|
next if $type ne 'SRV'; |
1081
|
0
|
|
|
|
|
0
|
my ($prio,$host,$port) = @$_; |
1082
|
0
|
|
|
|
|
0
|
my $family = ip_is_v46($host); |
1083
|
|
|
|
|
|
|
push @res, lock_ref_keys({ |
1084
|
0
|
0
|
|
|
|
0
|
%{$q->{rec}}, |
|
0
|
|
|
|
|
0
|
|
1085
|
|
|
|
|
|
|
prio => $prio, |
1086
|
|
|
|
|
|
|
host => $host, |
1087
|
|
|
|
|
|
|
addr => $family ? $host : undef, |
1088
|
|
|
|
|
|
|
port => $port, |
1089
|
|
|
|
|
|
|
family => $family, |
1090
|
|
|
|
|
|
|
}); |
1091
|
|
|
|
|
|
|
} |
1092
|
0
|
|
|
|
|
0
|
for(my $i=0; $i<@res; $i++) { |
1093
|
0
|
0
|
|
|
|
0
|
$res[$i]{family} and next; |
1094
|
0
|
0
|
|
|
|
0
|
my $ipt = $addr2ip{$res[$i]{host}} or next; |
1095
|
0
|
|
|
|
|
0
|
my $r = splice(@res,$i,1); |
1096
|
0
|
|
|
|
|
0
|
for(@$ipt) { |
1097
|
0
|
|
|
|
|
0
|
my ($ip,$type) = @$_; |
1098
|
0
|
0
|
|
|
|
0
|
splice(@res,$i,0, lock_ref_keys({ |
1099
|
|
|
|
|
|
|
%$r, |
1100
|
|
|
|
|
|
|
addr => $ip, |
1101
|
|
|
|
|
|
|
family => $type eq 'A' ? AF_INET : AF_INET6, |
1102
|
|
|
|
|
|
|
})); |
1103
|
0
|
|
|
|
|
0
|
$i++; |
1104
|
|
|
|
|
|
|
} |
1105
|
0
|
|
|
|
|
0
|
$i--; |
1106
|
|
|
|
|
|
|
} |
1107
|
0
|
|
|
|
|
0
|
for my $r (@res) { |
1108
|
0
|
0
|
|
|
|
0
|
if ($_->{family}) { |
1109
|
|
|
|
|
|
|
# done: host in SRV record is already IP address |
1110
|
0
|
|
|
|
|
0
|
push @$results, $r; |
1111
|
0
|
|
|
|
|
0
|
next; |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
# need to resolve host in SRV record - put queries on top |
1115
|
0
|
|
|
|
|
0
|
for my $type (CAN_IPV6 ? qw(AAAA A) : qw(A)) { |
1116
|
|
|
|
|
|
|
unshift @$queries, { |
1117
|
|
|
|
|
|
|
type => $type, |
1118
|
|
|
|
|
|
|
name => $r->{host}, |
1119
|
0
|
0
|
|
|
|
0
|
rec => lock_ref_keys({ |
1120
|
|
|
|
|
|
|
%$r, |
1121
|
|
|
|
|
|
|
family => $type eq 'A' ? AF_INET : AF_INET6, |
1122
|
|
|
|
|
|
|
}) |
1123
|
|
|
|
|
|
|
}; |
1124
|
|
|
|
|
|
|
} |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
} elsif ($q->{type} eq 'AAAA' || $q->{type} eq 'A') { |
1128
|
0
|
|
|
|
|
0
|
for(@$ans) { |
1129
|
0
|
|
|
|
|
0
|
my ($type,$ip) = @$_; |
1130
|
|
|
|
|
|
|
push @$results, lock_ref_keys({ |
1131
|
0
|
0
|
|
|
|
0
|
%{$q->{rec}}, |
|
0
|
|
|
|
|
0
|
|
1132
|
|
|
|
|
|
|
addr => $ip, |
1133
|
|
|
|
|
|
|
family => $type eq 'A' ? AF_INET : AF_INET6, |
1134
|
|
|
|
|
|
|
}); |
1135
|
|
|
|
|
|
|
} |
1136
|
|
|
|
|
|
|
} else { |
1137
|
0
|
|
|
|
|
0
|
die "unknown type $q->{type}"; |
1138
|
|
|
|
|
|
|
} |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
after_answers: |
1142
|
0
|
0
|
|
|
|
0
|
if (!@$queries) { |
1143
|
|
|
|
|
|
|
# no more queries -> done |
1144
|
0
|
|
0
|
|
|
0
|
invoke_callback($state->{callback}, @$results && $results); |
1145
|
0
|
|
|
|
|
0
|
return; |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
# still queries -> send next to resolver |
1149
|
0
|
|
|
|
|
0
|
my $q = $queries->[0]; |
1150
|
0
|
|
|
|
|
0
|
DEBUG(52,'issue lookup for %s %s',$q->{type}, $q->{name}); |
1151
|
|
|
|
|
|
|
$state->{resolver}($q->{type}, $q->{name}, [ |
1152
|
0
|
|
|
|
|
0
|
\&__generic_resolver, |
1153
|
|
|
|
|
|
|
$state, |
1154
|
|
|
|
|
|
|
"$q->{type}:$q->{name}" |
1155
|
|
|
|
|
|
|
]); |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
my $NetDNSResolver; |
1159
|
|
|
|
|
|
|
sub __net_dns_resolver { |
1160
|
0
|
|
|
0
|
|
0
|
my $eventloop = shift; |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
# Create only a single resolver. |
1163
|
0
|
|
0
|
|
|
0
|
$NetDNSResolver ||= eval { |
|
|
|
0
|
|
|
|
|
1164
|
|
|
|
|
|
|
require Net::DNS; |
1165
|
|
|
|
|
|
|
Net::DNS->VERSION >= 0.56 or die "version too old, need 0.56+"; |
1166
|
|
|
|
|
|
|
Net::DNS::Resolver->new; |
1167
|
|
|
|
|
|
|
} || die "cannot create resolver: Net::DNS not available?: $@"; |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
my $dnsread = sub { |
1170
|
0
|
|
|
0
|
|
0
|
my ($sock,$callback) = @_; |
1171
|
0
|
|
|
|
|
0
|
my $q = $NetDNSResolver->bgread($sock); |
1172
|
0
|
|
|
|
|
0
|
$eventloop->delFD($sock); |
1173
|
0
|
|
|
|
|
0
|
my @ans; |
1174
|
0
|
|
|
|
|
0
|
for my $rr ( $q->answer ) { |
1175
|
0
|
0
|
0
|
|
|
0
|
if ($rr->type eq 'SRV' ) { |
|
|
0
|
|
|
|
|
|
1176
|
0
|
|
|
|
|
0
|
push @ans, [ |
1177
|
|
|
|
|
|
|
'SRV', |
1178
|
|
|
|
|
|
|
$rr->priority, |
1179
|
|
|
|
|
|
|
$rr->target, |
1180
|
|
|
|
|
|
|
$rr->port, |
1181
|
|
|
|
|
|
|
]; |
1182
|
|
|
|
|
|
|
} elsif ($rr->type eq 'A' || $rr->type eq 'AAAA') { |
1183
|
0
|
|
|
|
|
0
|
push @ans, [ $rr->type, $rr->address, $rr->name ]; |
1184
|
|
|
|
|
|
|
} |
1185
|
|
|
|
|
|
|
} |
1186
|
0
|
|
|
|
|
0
|
invoke_callback($callback,\@ans); |
1187
|
0
|
|
|
|
|
0
|
}; |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
return sub { |
1190
|
0
|
|
|
0
|
|
0
|
my ($type,$name,$callback) = @_; |
1191
|
0
|
|
|
|
|
0
|
my $sock = $NetDNSResolver->bgsend($name,$type); |
1192
|
0
|
|
|
|
|
0
|
$eventloop->addFD($sock, EV_READ, |
1193
|
|
|
|
|
|
|
[$dnsread, $sock, $callback], |
1194
|
|
|
|
|
|
|
'dns' |
1195
|
|
|
|
|
|
|
); |
1196
|
0
|
|
|
|
|
0
|
}; |
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
########################################################################### |
1201
|
|
|
|
|
|
|
# Net::SIP::Dispatcher::Packet |
1202
|
|
|
|
|
|
|
# Container for Queue entries in Net::SIP::Dispatchers queue |
1203
|
|
|
|
|
|
|
########################################################################### |
1204
|
|
|
|
|
|
|
package Net::SIP::Dispatcher::Packet; |
1205
|
|
|
|
|
|
|
use fields ( |
1206
|
43
|
|
|
|
|
238
|
'id', # transaction id, used for canceling delivery if response came in |
1207
|
|
|
|
|
|
|
'callid', # callid, used for canceling all deliveries for this call |
1208
|
|
|
|
|
|
|
'packet', # the packet which nees to be delivered |
1209
|
|
|
|
|
|
|
'dst_addr', # to which adress the packet gets delivered, is array-ref because |
1210
|
|
|
|
|
|
|
# the DNS/SRV lookup might return multiple addresses and protocols: |
1211
|
|
|
|
|
|
|
# [ { hash: proto, addr, port, family, host }, { ... }, ...] |
1212
|
|
|
|
|
|
|
'leg', # through which leg the packet gets delivered, same number |
1213
|
|
|
|
|
|
|
# of items like dst_addr |
1214
|
|
|
|
|
|
|
'retransmits', # array of retransmit time stamps, if undef no retransmit will be |
1215
|
|
|
|
|
|
|
# done, if [] no more retransmits can be done (trigger ETIMEDOUT) |
1216
|
|
|
|
|
|
|
# the last element in this array will not used for retransmit, but |
1217
|
|
|
|
|
|
|
# is the timestamp, when the delivery fails permanently |
1218
|
|
|
|
|
|
|
'callback', # callback for DSN (success, ETIMEDOUT...) |
1219
|
|
|
|
|
|
|
'proto', # list of possible protocols, default tcp and udp for sip: |
1220
|
43
|
|
|
43
|
|
388
|
); |
|
43
|
|
|
|
|
87
|
|
1221
|
|
|
|
|
|
|
|
1222
|
43
|
|
|
43
|
|
4879
|
use Net::SIP::Debug; |
|
43
|
|
|
|
|
102
|
|
|
43
|
|
|
|
|
196
|
|
1223
|
43
|
|
|
43
|
|
264
|
use Net::SIP::Util ':all'; |
|
43
|
|
|
|
|
85
|
|
|
43
|
|
|
|
|
7149
|
|
1224
|
43
|
|
|
43
|
|
271
|
use Hash::Util 'lock_ref_keys'; |
|
43
|
|
|
|
|
84
|
|
|
43
|
|
|
|
|
779
|
|
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
########################################################################### |
1227
|
|
|
|
|
|
|
# create new Dispatcher::Packet |
1228
|
|
|
|
|
|
|
# Args: ($class,%args) |
1229
|
|
|
|
|
|
|
# %args: hash with values according to fields |
1230
|
|
|
|
|
|
|
# for response packets leg and dst_addr must be set |
1231
|
|
|
|
|
|
|
# Returns: $self |
1232
|
|
|
|
|
|
|
########################################################################### |
1233
|
|
|
|
|
|
|
sub new { |
1234
|
196
|
|
|
196
|
|
1190
|
my ($class,%args) = @_; |
1235
|
196
|
|
|
|
|
445
|
my $now = delete $args{now}; |
1236
|
|
|
|
|
|
|
|
1237
|
196
|
|
|
|
|
602
|
my $self = fields::new( $class ); |
1238
|
196
|
|
|
|
|
19857
|
%$self = %args; |
1239
|
196
|
|
66
|
|
|
1143
|
$self->{id} ||= $self->{packet}->tid; |
1240
|
196
|
|
33
|
|
|
1764
|
$self->{callid} ||= $self->{packet}->callid; |
1241
|
|
|
|
|
|
|
|
1242
|
196
|
|
|
|
|
408
|
my $addr = $self->{dst_addr}; |
1243
|
196
|
100
|
|
|
|
21040
|
if (!$addr) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
} elsif (!ref($addr)) { |
1245
|
0
|
|
|
|
|
0
|
my @si = sip_uri2sockinfo($addr); |
1246
|
0
|
0
|
|
|
|
0
|
$self->{dst_addr} = [ lock_ref_keys({ |
1247
|
|
|
|
|
|
|
proto => $si[0], |
1248
|
|
|
|
|
|
|
host => $si[1], |
1249
|
|
|
|
|
|
|
addr => $si[3] ? $si[1] : undef, |
1250
|
|
|
|
|
|
|
port => $si[2], |
1251
|
|
|
|
|
|
|
family => $si[3], |
1252
|
|
|
|
|
|
|
}) ]; |
1253
|
|
|
|
|
|
|
} elsif (ref($addr) eq 'HASH') { |
1254
|
83
|
|
|
|
|
258
|
$self->{dst_addr} = [ $addr ]; |
1255
|
|
|
|
|
|
|
} else { |
1256
|
|
|
|
|
|
|
# assume its already in the expected format, i.e. list of hashes |
1257
|
|
|
|
|
|
|
} |
1258
|
196
|
100
|
|
|
|
655
|
if ( my $leg = $self->{leg} ) { |
1259
|
122
|
50
|
|
|
|
1021
|
$self->{leg} = [ $leg ] if UNIVERSAL::can( $leg,'deliver' ); |
1260
|
|
|
|
|
|
|
} |
1261
|
|
|
|
|
|
|
|
1262
|
196
|
|
100
|
|
|
1426
|
$self->{dst_addr} ||= []; |
1263
|
196
|
|
100
|
|
|
805
|
$self->{leg} ||= []; |
1264
|
196
|
|
|
|
|
597
|
return $self; |
1265
|
|
|
|
|
|
|
} |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
########################################################################### |
1268
|
|
|
|
|
|
|
# prepare retransmit infos if dispatcher handles retransmits itself |
1269
|
|
|
|
|
|
|
# Args: ($self;$now) |
1270
|
|
|
|
|
|
|
# $now: current time |
1271
|
|
|
|
|
|
|
# Returns: NONE |
1272
|
|
|
|
|
|
|
########################################################################### |
1273
|
|
|
|
|
|
|
sub prepare_retransmits { |
1274
|
189
|
|
|
189
|
|
362
|
my Net::SIP::Dispatcher::Packet $self = shift; |
1275
|
189
|
100
|
100
|
|
|
1429
|
return if $self->{leg}[0] && ! $self->{leg}[0]->do_retransmits; |
1276
|
|
|
|
|
|
|
|
1277
|
137
|
|
|
|
|
276
|
my $now = shift; |
1278
|
137
|
|
|
|
|
236
|
my $p = $self->{packet}; |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
# RFC3261, 17.1.1.2 (final response to INVITE) -> T1=0.5, T2=4 |
1281
|
|
|
|
|
|
|
# RFC3261, 17.1.2.2 (non-INVITE requests) -> T1=0.5, T2=4 |
1282
|
|
|
|
|
|
|
# RFC3261, 17.1.1.2 (INVITE request) -> T1=0.5, T2=undef |
1283
|
|
|
|
|
|
|
# no retransmit -> T1=undef |
1284
|
|
|
|
|
|
|
|
1285
|
137
|
|
|
|
|
254
|
my ($t1,$t2); |
1286
|
137
|
100
|
|
|
|
490
|
if ( $p->is_response ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1287
|
39
|
100
|
100
|
|
|
130
|
if ( $p->code > 100 && $p->cseq =~m{\sINVITE$} ) { |
1288
|
|
|
|
|
|
|
# this is a final response to an INVITE |
1289
|
|
|
|
|
|
|
# this is the only type of response which gets retransmitted |
1290
|
|
|
|
|
|
|
# (until I get an ACK) |
1291
|
19
|
|
|
|
|
60
|
($t1,$t2) = (0.500,4); |
1292
|
|
|
|
|
|
|
} |
1293
|
|
|
|
|
|
|
} elsif ( $p->method eq 'INVITE' ) { |
1294
|
|
|
|
|
|
|
# INVITE request |
1295
|
40
|
|
|
|
|
126
|
($t1,$t2) = (0.500,undef); |
1296
|
|
|
|
|
|
|
} elsif ( $p->method eq 'ACK' ) { |
1297
|
|
|
|
|
|
|
# no retransmit of ACKs |
1298
|
|
|
|
|
|
|
} else { |
1299
|
|
|
|
|
|
|
# non-INVITE request |
1300
|
34
|
|
|
|
|
106
|
($t1,$t2) = (0.500,4); |
1301
|
|
|
|
|
|
|
} |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
# no retransmits? |
1304
|
137
|
100
|
|
|
|
543
|
$t1 || return; |
1305
|
|
|
|
|
|
|
|
1306
|
93
|
|
66
|
|
|
923
|
$now ||= time(); |
1307
|
93
|
|
|
|
|
274
|
my $expire = $now + 64*$t1; |
1308
|
93
|
|
|
|
|
213
|
my $to = $t1; |
1309
|
93
|
|
|
|
|
317
|
my $rtm = $now + $to; |
1310
|
|
|
|
|
|
|
|
1311
|
93
|
|
|
|
|
158
|
my @retransmits; |
1312
|
93
|
|
|
|
|
297
|
while ( $rtm < $expire ) { |
1313
|
770
|
|
|
|
|
1138
|
push @retransmits, $rtm; |
1314
|
770
|
|
|
|
|
1092
|
$to *= 2; |
1315
|
770
|
100
|
100
|
|
|
2000
|
$to = $t2 if $t2 && $to>$t2; |
1316
|
770
|
|
|
|
|
1233
|
$rtm += $to |
1317
|
|
|
|
|
|
|
} |
1318
|
93
|
|
|
|
|
388
|
DEBUG( 100,"retransmits $now + ".join( " ", map { $_ - $now } @retransmits )); |
|
770
|
|
|
|
|
3040
|
|
1319
|
93
|
|
|
|
|
796
|
$self->{retransmits} = \@retransmits; |
1320
|
|
|
|
|
|
|
} |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
########################################################################### |
1325
|
|
|
|
|
|
|
# use next dst_addr (eg if previous failed) |
1326
|
|
|
|
|
|
|
# Args: $self |
1327
|
|
|
|
|
|
|
# Returns: $addr |
1328
|
|
|
|
|
|
|
# $addr: new address it will use or undef if no more addresses available |
1329
|
|
|
|
|
|
|
########################################################################### |
1330
|
|
|
|
|
|
|
sub use_next_dstaddr { |
1331
|
0
|
|
|
0
|
|
0
|
my Net::SIP::Dispatcher::Packet $self = shift; |
1332
|
0
|
|
0
|
|
|
0
|
my $addr = $self->{dst_addr} || return; |
1333
|
0
|
|
|
|
|
0
|
shift(@$addr); |
1334
|
0
|
|
0
|
|
|
0
|
my $leg = $self->{leg} || return; |
1335
|
0
|
|
|
|
|
0
|
shift(@$leg); |
1336
|
0
|
|
0
|
|
|
0
|
return @$addr && $addr->[0]; |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
########################################################################### |
1340
|
|
|
|
|
|
|
# trigger callback to upper layer |
1341
|
|
|
|
|
|
|
# Args: ($self;$errno) |
1342
|
|
|
|
|
|
|
# $errno: Errno |
1343
|
|
|
|
|
|
|
# Returns: $callback_done |
1344
|
|
|
|
|
|
|
# $callback_done: true if callback was triggered, if no callback existed |
1345
|
|
|
|
|
|
|
# returns false |
1346
|
|
|
|
|
|
|
########################################################################### |
1347
|
|
|
|
|
|
|
sub trigger_callback { |
1348
|
88
|
|
|
88
|
|
161
|
my Net::SIP::Dispatcher::Packet $self = shift; |
1349
|
88
|
|
|
|
|
144
|
my $error = shift; |
1350
|
88
|
|
100
|
|
|
334
|
my $cb = $self->{callback} || return; |
1351
|
45
|
|
|
|
|
256
|
invoke_callback( $cb,$error,$self); |
1352
|
45
|
|
|
|
|
394
|
return 1; |
1353
|
|
|
|
|
|
|
} |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
########################################################################### |
1356
|
|
|
|
|
|
|
# return transaction id of packet |
1357
|
|
|
|
|
|
|
# Args: $self |
1358
|
|
|
|
|
|
|
# Returns: $tid |
1359
|
|
|
|
|
|
|
########################################################################### |
1360
|
|
|
|
|
|
|
sub tid { |
1361
|
45
|
|
|
45
|
|
76
|
my Net::SIP::Dispatcher::Packet $self = shift; |
1362
|
45
|
|
|
|
|
169
|
return $self->{packet}->tid; |
1363
|
|
|
|
|
|
|
} |
1364
|
|
|
|
|
|
|
1; |