File Coverage

blib/lib/Net/SIP/Dispatcher.pm
Criterion Covered Total %
statement 379 583 65.0
branch 143 280 51.0
condition 60 136 44.1
subroutine 43 58 74.1
pod 12 17 70.5
total 637 1074 59.3


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   249 use strict;
  43         73  
  43         1054  
12 43     43   169 use warnings;
  43         69  
  43         1802  
13              
14             package Net::SIP::Dispatcher;
15             use fields (
16             # interface to outside
17 43         169 '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   182 );
  43         63  
29              
30 43     43   20905 use Net::SIP::Leg;
  43         143  
  43         1277  
31 43     43   270 use Net::SIP::Util ':all';
  43         75  
  43         7220  
32 43     43   267 use Net::SIP::Dispatcher::Eventloop;
  43         73  
  43         1818  
33 43     43   210 use Errno qw(EHOSTUNREACH ETIMEDOUT ENOPROTOOPT);
  43         79  
  43         1627  
34 43     43   205 use IO::Socket;
  43         73  
  43         304  
35 43     43   19256 use List::Util 'first';
  43         80  
  43         2519  
36 43     43   263 use Hash::Util 'lock_ref_keys';
  43         101  
  43         169  
37 43     43   1970 use Carp 'croak';
  43         78  
  43         1576  
38 43     43   228 use Net::SIP::Debug;
  43         110  
  43         320  
39 43     43   278 use Scalar::Util 'weaken';
  43         113  
  43         2004  
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   247 use constant SRV_PRIO_UNDEF => 0x10000;
  43         74  
  43         75581  
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 493 my ($class,$legs,$eventloop,%args) = @_;
68              
69             my ($outgoing_proxy,$do_retransmits,$domain2proxy,$dnsresolv) = delete
70 57         375 @args{qw( outgoing_proxy do_retransmits domain2proxy dnsresolv)};
71 57 50       230 die "bad args: ".join( ' ',keys %args ) if %args;
72              
73 57   33     236 $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     538 $domain2proxy ||= {};
78 57         230 foreach ( values %$domain2proxy ) {
79 30 50       130 if ( ref($_) ) {
80             # should be \@list of [ prio,proto,ip,port,?family ]
81             } else {
82 30 50       129 my ($proto,$host,$port,$family) = sip_uri2sockinfo($_)
83             or croak( "invalid entry in domain2proxy: $_" );
84 30 0 0     100 $port ||= $proto && $proto eq 'tls' ? 5061:5060;
      33        
85 30 50       141 $_ = [ map { lock_ref_keys({
  48 100       823  
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         482 my $self = fields::new($class);
97 57 50       6923 %$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         1227 $self->add_leg( @$legs );
109              
110 57 50       211 $self->outgoing_proxy($outgoing_proxy) if $outgoing_proxy;
111              
112             # regularly prune queue
113             my $sub = sub {
114 175     175   805 my ($self,$timer) = @_;
115 175 50       725 if ( $self ) {
116 175         3166 $self->queue_expire( $self->{eventloop}->looptime );
117             } else {
118 0         0 $timer->cancel;
119             }
120 57         1029 };
121 57         493 my $cb = [ $sub,$self ];
122 57         412 weaken( $cb->[1] );
123 57         823 $self->{disp_expire} = $self->add_timer( 1,$cb,1,'disp_expire' );
124              
125 57         301 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 177 my Net::SIP::Dispatcher $self = shift;
170 59 50       280 if ( my $receiver = shift ) {
171 59 100       240 if ( my $sub = UNIVERSAL::can($receiver,'receive' )) {
172             # Object with method receive()
173 5         12 $receiver = [ $sub,$receiver ]
174             }
175 59         1059 $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 293 my Net::SIP::Dispatcher $self = shift;
192 111         279 my $legs = $self->{legs};
193 111         530 foreach my $arg (@_) {
194              
195 59         126 my $leg;
196             # if it is not a leg yet create one based
197             # on the arguments
198 59 50       1060 if ( UNIVERSAL::isa( $arg,'Net::SIP::Leg' )) {
    0          
    0          
199             # already a leg
200 59         187 $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         138 push @$legs, $leg;
214              
215 59 50       881 if (my $socketpool = $leg->socketpool) {
216             my $cb = sub {
217             # don't crash Dispatcher on bad or unexpected packets
218 209 50   209   408 eval {
219 209         545 my ($self,$leg,$packet,$from) = @_;
220 209 50       688 $self || return;
221              
222 209 50       1342 ($packet,$from) = $leg->receive($packet,$from) or return;
223              
224 209 100       1041 if ($packet->is_request) {
225             # add received and rport to top via
226             $packet->scan_header( via => [ sub {
227 71         195 my ($vref,$hdr) = @_;
228 71 50       273 return if $$vref++;
229 71         508 my ($d,$h) = sip_hdrval2parts(via => $hdr->{value});
230 71 50       878 my ($host,$port) = $d =~m{^SIP/2\S+\s+(\S+)$}
231             ? ip_string2parts($1):();
232 71         156 my %nh;
233 71 50 33     423 if ( exists $h->{rport} and ! defined $h->{rport}) {
234 0         0 $nh{rport} = $from->{port};
235             }
236 71 50       448 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 71 50       394 if (%nh) {
245 0         0 $hdr->{value} = sip_parts2hdrval('via',$d,{ %$h,%nh});
246 0         0 $hdr->set_modified;
247             }
248 71         1395 }, \( my $cvia )]);
249             }
250              
251             # handle received packet
252 209         1730 $self->receive( $packet,$leg,$from );
253 209         2656 1;
254             } or DEBUG(1,"dispatcher croaked: $@");
255 59         1130 };
256 59         259 $cb = [ $cb,$self,$leg ];
257 59         275 weaken($cb->[1]);
258 59         156 weaken($cb->[2]);
259 59         377 $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 133 my Net::SIP::Dispatcher $self = shift;
272 53         129 my $legs = $self->{legs};
273 53         407 foreach my $leg (@_) {
274 53         201 @$legs = grep { $_ != $leg } @$legs;
  53         276  
275 53 50       255 if ( my $pool = $leg->socketpool ) {
276 53         254 $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 350 my Net::SIP::Dispatcher $self = shift;
296 157 100       483 return @{ $self->{legs} } if ! @_; # shortcut
  155         769  
297              
298 2         6 my %args = @_;
299 2         3 my @rv;
300 2         3 foreach my $leg (@{ $self->{legs} }) {
  2         5  
301 2 50       9 push @rv,$leg if $leg->match(\%args);
302             }
303 2         6 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         11 my $legs = $self->{legs};
320 7 50       21 if (ref($_[-1]) eq 'SCALAR') {
321 7         11 my $dict = pop @_;
322 7         14 $$dict = join("|",map { $_->key } @$legs);
  19         44  
323             }
324 7         12 my @result;
325 7         12 for(@_) {
326 14         17 my $i;
327 14         28 for($i=$#$legs;$i>=0;$i--) {
328 31 100       62 last if $legs->[$i] == $_;
329             }
330 14         20 push @result,$i;
331             }
332 7         17 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 377 my Net::SIP::Dispatcher $self = shift;
366 124         1446 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 391 my Net::SIP::Dispatcher $self = shift;
387 196         1237 my ($packet,%more_args) = @_;
388 196         523 my $now = delete $more_args{now};
389 196         393 my $do_retransmits = delete $more_args{do_retransmits};
390 196 100       772 $do_retransmits = $self->{do_retransmits} if !defined $do_retransmits;
391              
392 196         1050 DEBUG( 100,"deliver $packet" );
393              
394 196 100       1334 if ( $packet->is_response ) {
395             # cache response for 32 sec (64*T1)
396 76 50       253 if ( $do_retransmits ) {
397             my $cid = join( "\0",
398 76         237 map { $packet->get_header($_) }
  304         691  
399             qw( cseq call-id from to )
400             );
401 76   33     1202 $self->{response_cache}{$cid} = {
402             packet => $packet,
403             expire => ( $now ||= time()) +32
404             };
405             }
406             }
407              
408 196         2327 my $new_entry = Net::SIP::Dispatcher::Packet->new(
409             packet => $packet,
410             %more_args
411             );
412              
413 196 100       1098 $new_entry->prepare_retransmits( $now ) if $do_retransmits;
414              
415 196         373 push @{ $self->{queue}}, $new_entry;
  196         547  
416 196         1194 $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 340     340 1 685 my Net::SIP::Dispatcher $self = shift;
429 340         633 my ($callid,$id,$qentry);
430 340 100       994 if ( @_ == 2 ) {
431 48         312 my $typ = shift;
432 48 50       186 if ( $typ eq 'callid' ) { $callid = shift }
  48 0       120  
    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 292         660 $id = shift;
440 292 100       820 if ( ref($id)) {
441 133         235 $qentry = $id;
442 133         252 $id = undef;
443             }
444             }
445 340         699 my $q = $self->{queue};
446 340         635 my $qn = @$q;
447 340 100       1182 if ( $qentry ) {
    100          
    50          
448             # it's a *::Dispatcher::Packet
449 133         734 DEBUG( 100,"cancel packet id: $qentry->{id}" );
450 133         349 @$q = grep { $_ != $qentry } @$q;
  136         569  
451             } elsif ( defined $id ) {
452 43     43   366 no warnings; # $_->{id} can be undef
  43         87  
  43         3201  
453 159         1051 DEBUG( 100, "cancel packet id $id" );
454 159         419 @$q = grep { $_->{id} ne $id } @$q;
  60         666  
455             } elsif ( defined $callid ) {
456 43     43   252 no warnings; # $_->{callid} can be undef
  43         85  
  43         145205  
457 48         216 DEBUG( 100, "cancel packet callid $callid" );
458 48         131 @$q = grep { $_->{callid} ne $callid } @$q;
  4         18  
459             } else {
460 0         0 croak( "cancel_delivery w/o id" );
461             }
462 340         2474 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 428 my Net::SIP::Dispatcher $self = shift;
481 216         484 my ($packet,$leg,$from) = @_;
482              
483 216 100       612 if ( $packet->is_request ) {
484 78         213 my $cache = $self->{response_cache};
485 78 100       276 if ( %$cache ) {
486             my $cid = join( "\0",
487 48         159 map { $packet->get_header($_) }
  192         432  
488             qw( cseq call-id from to )
489             );
490              
491 48 100       253 if ( my $response = $cache->{$cid} ) {
492             # I have a cached response, use it
493             $self->deliver($response->{packet},
494 2         7 leg => $leg,
495             dst_addr => $from,
496             );
497 2         6 return;
498             }
499             }
500             }
501              
502 214         958 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 175     175 1 703 my Net::SIP::Dispatcher $self = shift;
516 175   33     1309 my $now = shift || $self->{eventloop}->looptime;
517              
518             # expire queue
519 175         498 my $queue = $self->{queue};
520 175         422 my (@nq,$changed,$min_expire);
521 175         554 foreach my $qe (@$queue) {
522              
523 10         19 my $retransmit;
524 10 50       44 if ( my $retransmits = $qe->{retransmits} ) {
525 10   66     98 while ( @$retransmits && $retransmits->[0] < $now ) {
526 9         39 $retransmit = shift(@$retransmits);
527             }
528              
529 10 50       36 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       27 if ( $retransmit ) {
540             # need to retransmit the packet
541 7         33 $self->__deliver( $qe );
542             }
543              
544 10         23 my $next_retransmit = $retransmits->[0];
545 10 100 66     53 if ( !defined($min_expire) || $next_retransmit<$min_expire ) {
546 9         20 $min_expire = $next_retransmit
547             }
548             }
549 10         30 push @nq,$qe;
550              
551             }
552 175 50       514 $self->{queue} = \@nq if $changed;
553              
554             # expire response cache
555 175         472 my $cache = $self->{response_cache};
556 175         648 foreach my $cid ( keys %$cache ) {
557 84         272 my $expire = $cache->{$cid}{expire};
558 84 50 100     699 if ( $expire < $now ) {
    100          
559 0         0 delete $cache->{$cid};
560             } elsif ( !defined($min_expire) || $expire<$min_expire ) {
561 68         202 $min_expire = $expire
562             }
563             }
564              
565             # return time to next expire for optimizations
566 175         1049 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 316     316   591 my Net::SIP::Dispatcher $self = shift;
585 316         483 my $qentry = shift;
586              
587             # loop until leg und dst_addr are known, when we call leg->deliver
588 316         749 my $leg = $qentry->{leg}[0];
589 316 50 66     1280 if ( $leg && @{ $qentry->{leg}}>1 ) {
  242         916  
590 0         0 DEBUG( 50,"picking first of multiple legs: ".join( " ", map { $_->dump } @{ $qentry->{leg}} ));
  0         0  
  0         0  
591             }
592 316         635 my $dst_addr = $qentry->{dst_addr}[0];
593              
594 316 100 66     5039 if ( ! $dst_addr || ! $leg) {
595              
596             # if explicit routes given use first route
597             # else resolve URI from request
598              
599 113         188 my $uri;
600 113         231 my $packet = $qentry->{packet};
601 113 50       389 if ( my ($route) = $packet->get_header( 'route' )) {
602 0         0 ($uri) = sip_hdrval2parts( route => $route );
603             } else {
604 113         433 $uri = $packet->uri;
605             }
606              
607 113         645 DEBUG( 100,"no dst_addr or leg yet, uri='$uri'" );
608              
609             my $callback = sub {
610 113     113   266 my ($self,$qentry,@error) = @_;
611 113 50       303 if ( @error ) {
612 0         0 $qentry->trigger_callback(@error);
613 0         0 return $self->cancel_delivery( $qentry );
614             } else {
615 113         556 $self->__deliver($qentry);
616             }
617 113         976 };
618             return $self->resolve_uri(
619             $uri,
620             $qentry->{dst_addr},
621             $qentry->{leg},
622             [ $callback, $self,$qentry ],
623             $qentry->{proto},
624 113         1002 );
625             }
626              
627 203 100 100     1161 if ($qentry->{retransmits} && ! $leg->do_retransmits) {
628 30         177 $qentry->{retransmits} = undef;
629             }
630              
631             # I have leg and addr, send packet thru leg to addr
632             my $cb = sub {
633 87     87   263 my ($self,$qentry,$error) = @_;
634 87 50       266 $self || return;
635 87 50 33     535 if ( !$error && $qentry->{retransmits} ) {
636             # remove from queue even if timeout
637 0         0 $self->cancel_delivery( $qentry );
638             }
639 87         340 $qentry->trigger_callback( $error );
640 203         1469 };
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 203 50       620 $DEBUG && DEBUG(50,"deliver through leg ".$leg->dump.' @'
646             .ip_parts2string($dst_addr));
647 203         875 weaken( my $rself = \$self );
648 203         562 $cb = [ $cb,$self,$qentry ];
649 203         596 weaken( $cb->[1] );
650 203         1305 $leg->deliver( $qentry->{packet},$dst_addr,$cb );
651              
652 203 100       2302 if ( !$qentry->{retransmits} ) {
653             # remove from queue if no timeout
654 133         690 $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 273 my Net::SIP::Dispatcher $self = shift;
675 120         572 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         669 my ($domain,$user,$sip_proto,$param) = sip_uri2parts($uri);
679 120 50       715 $domain or do {
680 0         0 DEBUG( 50,"bad URI '$uri'" );
681 0         0 return invoke_callback($callback, EHOSTUNREACH );
682             };
683              
684 120         233 my @proto;
685 120         232 my $default_port = 5060;
686 120 100       619 if ( $sip_proto eq 'sips' ) {
    100          
687 10         23 $default_port = 5061;
688 10         79 @proto = 'tls';
689             } elsif ( my $p = $param->{transport} ) {
690             # explicit spec of proto
691 4         58 @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         599 @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     518 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     330 $dst_addr ||= [];
715 120   50     1074 $allowed_legs ||= [ $self->get_legs ];
716 120 100       400 if ( @$legs ) {
717 39         109 my %allowed = map { $_ => 1 } @$legs;
  39         200  
718 39         99 @$allowed_legs = grep { $allowed{$_} } @$allowed_legs;
  39         186  
719             }
720 120 50       333 @$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         241 my $ip_addr = $param->{maddr};
726             {
727 120 100       206 my ($host,$port,$family) = ip_string2parts($domain, $ip_addr ? 1:0);
  120         536  
728 120 100       406 $default_port = $port if defined $port;
729 120 100       287 if ($family) {
730 68   33     588 $ip_addr ||= $host;
731 68         297 $domain = ip_ptr($host,$family);
732             } else {
733 52         101 $domain = $host;
734             }
735             }
736 120         678 DEBUG( 100,"domain=$domain" );
737              
738             # do we have a fixed proxy for the domain or upper domain?
739 120 50       436 if ( ! @$dst_addr ) {
740 120         303 my $d2p = $self->{domain2proxy};
741 120 100 66     793 if ( $d2p && %$d2p ) {
742 81         229 my $dom = $domain;
743 81         181 my $addr = $d2p->{$dom}; # exact match
744 81         219 while ( ! $addr) {
745 221 100       826 $dom =~s{^[^\.]+\.}{} or last;
746 183         402 $addr = $d2p->{ "*.$dom" };
747             }
748 81   100     329 $addr ||= $d2p->{ $dom = '*'}; # catch-all
749 81 100       198 if ( $addr ) {
750 45         202 DEBUG( 50,"setting dst_addr from domain specific proxy for domain $dom" );
751 45         131 @$dst_addr = @$addr;
752             }
753             }
754             }
755              
756             # do we have a global outgoing proxy?
757 120 50 66     1013 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     593 if ( !@$dst_addr && $ip_addr ) {
766 75         286 DEBUG( 50,"setting dst_addr from URI because IP address given" );
767 75         202 @$dst_addr = ( $ip_addr );
768             }
769              
770             # is param maddr set?
771 120 100       420 if ( my $ip = $param->{maddr} ) {
772 7 50       50 @$dst_addr = ($ip) if ip_is_v46($ip);
773             }
774              
775              
776             # entries are hashes of prio,proto,host,addr,port,family
777 120         211 my @resp;
778 120         518 foreach my $addr ( @$dst_addr ) {
779 147 100       374 if ( ref($addr)) {
780 72         162 push @resp,$addr; # right format: see domain2proxy
781             } else {
782 75 50       303 my ($proto,$host,$port,$family) = sip_uri2sockinfo($addr)
783             or next;
784 75 50 33     999 $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       899 push @resp, map { lock_ref_keys({
  139         1372  
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     1583 if (@resp and my $proto = $param->{transport} ) {
801 4         44 $proto = lc($proto);
802 4 50       68 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         26 @resp = grep { $_->{proto} ne 'udp' } @resp
  4         42  
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       58 return invoke_callback($callback, ENOPROTOOPT) if ! @resp;
814             }
815              
816 120         484 my @param = ( $dst_addr,$legs,$allowed_legs,$default_port,$callback );
817 120 50       348 if (@resp) {
818             # directly call __resolve_uri_final if all names are resolved
819             return __resolve_uri_final( @param,\@resp )
820 120 50       241 if ! grep { ! $_->{addr} } @resp;
  211         931  
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   395 my ($dst_addr,$legs,$allowed_legs,$default_port,$callback,$resp) = @_;
837 120 50       308 $DEBUG && DEBUG_DUMP( 100,$resp );
838              
839 120 50 33     804 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     597 $_->{port} ||= $default_port for(@$resp);
844              
845             # sort by prio
846             # FIXME: can contradict order in @proto
847 120         688 @$resp = sort { $a->{prio} <=> $b->{prio} } @$resp;
  91         427  
848              
849 120         446 @$dst_addr = ();
850 120         243 @$legs = ();
851 120         279 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   1527 )} @$allowed_legs;
  223         1572  
859              
860 211 100       922 if ( $leg ) {
861 126         272 push @$dst_addr, $r;
862 126         268 push @$legs,$leg;
863             } else {
864 85         441 DEBUG(50,"no leg with $r->{proto} to %s", ip_parts2string($r));
865             }
866             }
867              
868 120 50       762 return invoke_callback( $callback, EHOSTUNREACH ) if !@$dst_addr;
869 120         381 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         360 '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   400 );
  43         102  
1221              
1222 43     43   4625 use Net::SIP::Debug;
  43         97  
  43         209  
1223 43     43   296 use Net::SIP::Util ':all';
  43         88  
  43         7662  
1224 43     43   274 use Hash::Util 'lock_ref_keys';
  43         91  
  43         968  
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   1136 my ($class,%args) = @_;
1235 196         491 my $now = delete $args{now};
1236              
1237 196         628 my $self = fields::new( $class );
1238 196         16858 %$self = %args;
1239 196   66     1146 $self->{id} ||= $self->{packet}->tid;
1240 196   33     1695 $self->{callid} ||= $self->{packet}->callid;
1241              
1242 196         402 my $addr = $self->{dst_addr};
1243 196 100       831 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         241 $self->{dst_addr} = [ $addr ];
1255             } else {
1256             # assume its already in the expected format, i.e. list of hashes
1257             }
1258 196 100       615 if ( my $leg = $self->{leg} ) {
1259 122 50       849 $self->{leg} = [ $leg ] if UNIVERSAL::can( $leg,'deliver' );
1260             }
1261              
1262 196   100     1180 $self->{dst_addr} ||= [];
1263 196   100     756 $self->{leg} ||= [];
1264 196         598 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   375 my Net::SIP::Dispatcher::Packet $self = shift;
1275 189 100 100     1948 return if $self->{leg}[0] && ! $self->{leg}[0]->do_retransmits;
1276              
1277 138         287 my $now = shift;
1278 138         265 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 138         249 my ($t1,$t2);
1286 138 100       415 if ( $p->is_response ) {
    100          
    100          
1287 40 100 100     202 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         64 ($t1,$t2) = (0.500,4);
1292             }
1293             } elsif ( $p->method eq 'INVITE' ) {
1294             # INVITE request
1295 40         118 ($t1,$t2) = (0.500,undef);
1296             } elsif ( $p->method eq 'ACK' ) {
1297             # no retransmit of ACKs
1298             } else {
1299             # non-INVITE request
1300 34         112 ($t1,$t2) = (0.500,4);
1301             }
1302              
1303             # no retransmits?
1304 138 100       421 $t1 || return;
1305              
1306 93   66     759 $now ||= time();
1307 93         325 my $expire = $now + 64*$t1;
1308 93         170 my $to = $t1;
1309 93         265 my $rtm = $now + $to;
1310              
1311 93         190 my @retransmits;
1312 93         315 while ( $rtm < $expire ) {
1313 770         1159 push @retransmits, $rtm;
1314 770         927 $to *= 2;
1315 770 100 100     2076 $to = $t2 if $t2 && $to>$t2;
1316 770         1418 $rtm += $to
1317             }
1318 93         403 DEBUG( 100,"retransmits $now + ".join( " ", map { $_ - $now } @retransmits ));
  770         2779  
1319 93         576 $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 87     87   1492 my Net::SIP::Dispatcher::Packet $self = shift;
1349 87         241 my $error = shift;
1350 87   100     431 my $cb = $self->{callback} || return;
1351 45         284 invoke_callback( $cb,$error,$self);
1352 45         382 return 1;
1353             }
1354              
1355             ###########################################################################
1356             # return transaction id of packet
1357             # Args: $self
1358             # Returns: $tid
1359             ###########################################################################
1360             sub tid {
1361 45     45   82 my Net::SIP::Dispatcher::Packet $self = shift;
1362 45         201 return $self->{packet}->tid;
1363             }
1364             1;