File Coverage

blib/lib/Net/SIP/Simple.pm
Criterion Covered Total %
statement 162 254 63.7
branch 39 110 35.4
condition 21 70 30.0
subroutine 30 37 81.0
pod 13 13 100.0
total 265 484 54.7


line stmt bran cond sub pod time code
1             #########################################################################
2             # Net::SIP::Simple
3             # simple methods for creation of UAC,UAS
4             # - register register Address
5             # - invite create new call
6             # - listen UAS, wait for incoming requests
7             # - create_registrar - create a simple registrar
8             # - create_stateless_proxy - create a simple stateless proxy
9             ###########################################################################
10              
11 43     43   233 use strict;
  43         69  
  43         1040  
12 43     43   166 use warnings;
  43         65  
  43         2134  
13              
14             package Net::SIP::Simple;
15             use fields (
16 43         149 'endpoint', # Net::SIP::Endpoint
17             'dispatcher', # Net::SIP::Dispatcher
18             'loop', # Net::SIP::Dispatcher::Eventloop or similar
19             'outgoing_proxy', # optional outgoing proxy (SIP URL)
20             'route', # more routes
21             'registrar', # optional registrar (addr:port)
22             'auth', # Auth data, see Net::SIP::Endpoint
23             'from', # SIP address of caller
24             'contact', # optional local contact address
25             'domain', # default domain for SIP addresses
26             'last_error', # last error
27             'options', # hash with field,values for response to OPTIONS request
28             'ua_cleanup', # cleanup callbacks
29 43     43   16960 );
  43         58536  
30              
31 43     43   5075 use Carp qw(croak);
  43         73  
  43         1776  
32 43     43   19028 use Net::SIP::Dispatcher;
  43         125  
  43         1244  
33 43     43   253 use Net::SIP::Dispatcher::Eventloop;
  43         88  
  43         1870  
34 43     43   18530 use Net::SIP::Endpoint;
  43         114  
  43         1161  
35 43     43   15780 use Net::SIP::Redirect;
  43         101  
  43         1058  
36 43     43   14809 use Net::SIP::Registrar;
  43         101  
  43         1149  
37 43     43   18437 use Net::SIP::StatelessProxy;
  43         106  
  43         1203  
38 43     43   15455 use Net::SIP::Authorize;
  43         107  
  43         1133  
39 43     43   14146 use Net::SIP::ReceiveChain;
  43         103  
  43         1013  
40 43     43   243 use Net::SIP::Leg;
  43         68  
  43         748  
41             # crossref, because its derived from Net::SIP::Simple
42             # now load in Net::SIP
43             # use Net::SIP::Simple::Call;
44 43     43   16260 use Net::SIP::Simple::RTP;
  43         142  
  43         1241  
45 43     43   267 use Net::SIP::Util qw( :all );
  43         75  
  43         6701  
46 43     43   257 use List::Util 'first';
  43         78  
  43         2012  
47 43     43   223 use Net::SIP::Debug;
  43         70  
  43         177  
48              
49             ###########################################################################
50             # create UA
51             # Args: ($class;%args)
52             # %args: misc args, all args are optional
53             # legs|leg - \@list of legs or single leg.
54             # leg can be (derived from) Net::SIP::Leg, a IO::Handle (socket),
55             # a hash reference for constructing Net::SIP::Leg or a string
56             # with a SIP address (i.e. sip:ip:port;transport=TCP)
57             # tls - common TLS settings used when creating a leg
58             # outgoing_proxy - specify outgoing proxy, will create leg if necessary
59             # proxy - alias to outgoing_proxy
60             # route|routes - \@list with SIP routes in right syntax ""...
61             # registrar - use registrar for registration
62             # auth - auth data: see Request->authorize for format
63             # from - myself, used for calls and registration
64             # contact - optional local contact address
65             # options - hash with fields,values for reply to OPTIONS request
66             # loop - predefined Net::SIP::Dispatcher::Eventloop, used if
67             # shared between UAs
68             # dispatcher - predefined Net::SIP::Dispatcher, used if
69             # shared between UAs
70             # domain - domain used if from/to.. do not contain domain
71             # domain2proxy - hash of { domain => proxy }
72             # used to find proxy for domain. If nothing matches here
73             # DNS need to be used. Special domain '*' catches all
74             # d2p - alias for domain2proxy
75             # Returns: $self
76             # Comment:
77             # FIXME
78             # If more than one leg is given (e.g. legs+outgoing_proxy) than you have
79             # to provide a function to find out, which leg is used to send out a request
80             ###########################################################################
81             sub new {
82 54     54 1 1121 my ($class,%args) = @_;
83 54         247 my $auth = delete $args{auth};
84 54         134 my $registrar = delete $args{registrar};
85 54         136 my $tls = delete $args{tls};
86              
87 54         251 my $ua_cleanup = [];
88 54         281 my $self = fields::new( $class );
89              
90 54   50     8969 my $options = delete $args{options} || {};
91             {
92 54         150 @{$options}{ map { lc } keys(%$options) } = values(%$options); # lc keys
  54         192  
  54         149  
  0         0  
93 54         1538 my %default_options = (
94             allow => 'INVITE, ACK, CANCEL, OPTIONS, BYE',
95             accept => 'application/sdp',
96             'accept-encoding' => '',
97             'accept-language' => 'en',
98             supported => '',
99             );
100 54         430 while ( my ($k,$v) = each %default_options ) {
101 270 50       1147 $options->{$k} = $v if ! defined $options->{$k};
102             }
103             }
104              
105 54         123 my $disp = delete $args{dispatcher};
106             my $loop = $disp && $disp->loop
107             || delete $args{loop}
108 54   33     2551 || Net::SIP::Dispatcher::Eventloop->new;
109 54   33     287 my $proxy = delete $args{outgoing_proxy} || delete $args{proxy};
110 54   66     413 my $d2p = delete $args{domain2proxy} || delete $args{d2p};
111 54   33     2439 $disp ||= Net::SIP::Dispatcher->new(
112             [],
113             $loop,
114             domain2proxy => $d2p,
115             );
116              
117 54   66     1232 my $legs = delete $args{legs} || delete $args{leg};
118 54 100 66     674 $legs = [ $legs ] if $legs && ref($legs) ne 'ARRAY';
119 54   50     230 $legs ||= [];
120              
121             my $host2ip = sub {
122 0     0   0 my $host = shift;
123 0         0 my $ip;
124 0   0     0 $disp->dns_host2ip($host,sub { $ip = shift // \0 });
  0         0  
125 0         0 $loop->loop(15,\$ip);
126 0 0 0     0 die "failed to resolve $host".($ip ? '':' - timed out')
    0          
127             if ! defined $ip || ref($ip);
128 0         0 return ($ip,ip_is_v46($ip));
129 54         904 };
130              
131 54 50       338 foreach ($legs ? @$legs : ()) {
132 54 100       1520 if ( UNIVERSAL::isa( $_, 'Net::SIP::Leg' )) {
    50          
    0          
    0          
133             # keep
134             } elsif ( UNIVERSAL::isa( $_, 'IO::Handle' )) {
135             # socket
136 2         24 $_ = Net::SIP::Leg->new(
137             sock => $_,
138             tls => $tls
139             )
140             } elsif ( UNIVERSAL::isa( $_, 'HASH' )) {
141             # create leg from hash
142 0         0 $_ = Net::SIP::Leg->new(tls => $tls, %$_)
143             } elsif (my ($proto,$host,$port,$family) = sip_uri2sockinfo($_)) {
144 0 0       0 (my $addr,$family) = $family ? ($host,$family) : $host2ip->($host);
145 0         0 $_ = Net::SIP::Leg->new(
146             proto => $proto,
147             tls => $tls,
148             host => $host,
149             addr => $addr,
150             port => $port,
151             family => $family
152             );
153             } else {
154 0         0 die "invalid leg specification: $_";
155             }
156             }
157              
158 54         381 for my $dst ($registrar, $proxy) {
159 108 50       312 $dst or next;
160 0 0   0   0 first { $_->can_deliver_to($dst) } @$legs and next;
  0         0  
161 0         0 my ($proto,$host,$port,$family) = sip_uri2sockinfo($dst);
162 0 0       0 (my $addr,$family) = $family ? ($host,$family) : $host2ip->($host);
163 0         0 push @$legs, Net::SIP::Leg->new(
164             proto => $proto,
165             tls => $tls,
166             dst => {
167             host => $host,
168             addr => $addr,
169             port => $port,
170             family => $family,
171             }
172             );
173             }
174              
175 54 50       374 $disp->add_leg(@$legs) if @$legs;
176 54 50       894 $disp->outgoing_proxy($proxy) if $proxy;
177              
178             push @$ua_cleanup, [
179             sub {
180 53     53   130 my ($self,$legs) = @_;
181 53         545 $self->{dispatcher}->remove_leg(@$legs);
182             },
183 54 50       907 $self,$legs
184             ] if @$legs;
185              
186 54         1412 my $endpoint = Net::SIP::Endpoint->new( $disp );
187              
188 54   33     572 my $routes = delete $args{routes} || delete $args{route};
189 54         167 my $from = delete $args{from};
190 54         97 my $contact = delete $args{contact};
191 54         370 my $domain = delete $args{domain};
192              
193 54 100       174 if ($from) {
194 38 100 66     761 if (!defined($domain) && $from =~m{\bsips?:[^@]+\@([\w\-\.]+)}) {
195 4         18 $domain = $1;
196             }
197 38 50 33     829 if ($from !~m{\s} && $from !~m{\@}) {
198 0 0       0 my $sip_proto = $disp->get_legs(proto => 'tls') ? 'sips' : 'sip';
199 0         0 $from = "$from <$sip_proto:$from\@$domain>";
200             }
201             }
202              
203 54 50       181 die "unhandled arguments: ".join(", ", keys %args) if %args;
204              
205 54         487 %$self = (
206             auth => $auth,
207             from => $from,
208             contact => $contact,
209             domain => $domain,
210             endpoint => $endpoint,
211             registrar => $registrar,
212             dispatcher => $disp,
213             loop => $loop,
214             route => $routes,
215             options => $options,
216             ua_cleanup => $ua_cleanup,
217             );
218 54         599 return $self;
219             }
220              
221             ###########################################################################
222             # cleanup object, e.g. remove legs it added to dispatcher
223             # Args: ($self)
224             # Returns: NONE
225             ###########################################################################
226             sub cleanup {
227 53     53 1 13114 my Net::SIP::Simple $self = shift;
228 53         121 while ( my $cb = shift @{ $self->{ua_cleanup} } ) {
  106         2174  
229 53         243 invoke_callback($cb,$self)
230             }
231 53         3048 %$self = ();
232             }
233              
234             ###########################################################################
235             # get last error or set it
236             # Args: ($self;$err)
237             # $err: if given will set error
238             # Returns: $last_error
239             ###########################################################################
240             sub error {
241 21     21 1 2963 my Net::SIP::Simple $self = shift;
242 21 100       82 if ( @_ ) {
243 1         3 $self->{last_error} = shift;
244             $DEBUG && DEBUG(100,Net::SIP::Debug::stacktrace(
245 1 50       3 "set error to ".$self->{last_error}) );
246             }
247 21         114 return $self->{last_error};
248             }
249              
250              
251             ###########################################################################
252             # mainloop
253             # Args: (;$timeout,@stopvar)
254             # $timeout: timeout, undef for no timeout. argument can be omitted
255             # @stopvar: @array of Scalar-REF, loop stops if one scalar is true
256             # Returns: NONE
257             ###########################################################################
258             sub loop {
259 140     140 1 4757 my Net::SIP::Simple $self = shift;
260 140         375 my ($timeout,@stopvar);
261 140         457 foreach (@_) {
262 218 100       893 if ( ref($_) ) {
    50          
263 123         406 push @stopvar,$_
264             } elsif ( defined($_)) {
265 95         249 $timeout = $_
266             }
267             }
268 140         837 return $self->{loop}->loop( $timeout,@stopvar );
269             }
270              
271             ###########################################################################
272             # add timer
273             # propagates to add_timer of wNet::SIP::Dispatcher, see there for detailed
274             # explanation of args
275             # Args: ($self,$when,$cb,$repeat)
276             # Returns: $timer
277             ###########################################################################
278             sub add_timer {
279 0     0 1 0 my Net::SIP::Simple $self = shift;
280 0         0 $self->{dispatcher}->add_timer( @_ );
281             }
282              
283             ###########################################################################
284             # control RTP behavior
285             # Args: ($self,$method,@arg)
286             # $method: Method name for behavior, e.g. calls Net::SIP::Simple::RTP::$method
287             # @arg: Arguments for method
288             # Returns: $cb
289             # $cb: callback structure
290             ###########################################################################
291             sub rtp {
292 73     73 1 7673 my Net::SIP::Simple $self = shift;
293 73         287 my ($method,@arg) = @_;
294 73   33     2067 my $sub = UNIVERSAL::can( 'Net::SIP::Simple::RTP',$method )
295             || UNIVERSAL::can( 'Net::SIP::Simple::RTP','media_'.$method )
296             || croak( "no such method '$method' in Net::SIP::Simple::RTP" );
297 73         745 return $sub->( @arg );
298             }
299              
300              
301             ###########################################################################
302             # Register UA at registrar
303             # waits until final response is received
304             # Args: ($self,%args)
305             # %args: Hash with keys..
306             # registrar: Register there, default $self->{registrar}
307             # from: use 'from' as lokal address, default $self->{from}
308             # leg: use given Net::SIP::Leg object for registration, default first leg
309             # cb_final: user defined callback when final response is received
310             # more args (expire...) will be forwarded to Net::SIP::Endpoint::register
311             # Returns: expires
312             # if user defined callback or failed expires will be undef
313             # otherwise it will be the expires value from the registrars response
314             ###########################################################################
315             sub register {
316 0     0 1 0 my Net::SIP::Simple $self = shift;
317 0         0 my %args = @_;
318              
319             my $registrar = delete $args{registrar} || $self->{registrar}
320 0   0     0 || croak( "no registrar" );
321 0         0 $registrar = sip_parts2uri(sip_uri2parts($registrar)); # normalize
322 0         0 my $leg = delete $args{leg};
323 0 0       0 if ( !$leg ) {
324             # use first leg which can deliver to registrar
325             ($leg) = $self->{dispatcher}->get_legs( sub => [
326             sub {
327 0     0   0 my ($addr,$leg) = @_;
328 0         0 return $leg->can_deliver_to($addr);
329             },
330 0         0 $registrar
331             ]);
332             }
333              
334             my $from = delete $args{from} || $self->{from}
335 0   0     0 || croak( "unknown from" );
336              
337 0   0     0 my $contact = delete $args{contact} || $self->{contact};
338 0 0       0 if ( ! $contact) {
339 0         0 $contact = $from;
340 0         0 my $local = $leg->laddr(2);
341 0 0       0 $contact.= '@'.$local unless $contact =~s{\@([^\s;,>]+)}{\@$local};
342             }
343              
344             my %rarg = (
345             from => $from,
346             registrar => $registrar,
347             contact => $contact,
348             auth => delete $args{auth} || $self->{auth},
349 0   0     0 );
350 0 0       0 %rarg = ( %rarg, %args ) if %args;
351              
352 0         0 my $cb_final = delete $rarg{cb_final};
353 0         0 my $stopvar = 0;
354 0   0     0 $cb_final ||= \$stopvar;
355              
356             my $cb = sub {
357 0     0   0 my ($self,$cb_final,$expires,$endpoint,$ctx,$errno,$code,$packet,$leg,$from) = @_;
358 0 0 0     0 if ( $code && $code =~m{^2\d\d} ) {
    0          
    0          
359             # use expires info on contact
360             # if none given use global expires header
361             # see rfc3261 10.3.8,10.2.4
362 0         0 my $exp;
363 0         0 for my $c ( $packet->get_header( 'contact' ) ) {
364 0         0 my ($addr,$p) = sip_hdrval2parts( contact => $c );
365 0 0       0 defined( my $e = $p->{expires} ) or next;
366 0 0       0 sip_uri_eq($addr,$contact) or next; # not me
367 0 0 0     0 $exp = $e if ! defined($exp) || $e < $exp;
368             }
369 0 0       0 $exp = $packet->get_header( 'Expires' ) if ! defined $exp;
370 0         0 $$expires = $exp;
371 0         0 invoke_callback( $cb_final, 'OK', expires => $exp, packet => $packet );
372              
373             } elsif ( $code ) {
374 0         0 $self->error( "Failed with code $code" );
375 0         0 invoke_callback( $cb_final, 'FAIL', code => $code, packet => $packet );
376             } elsif ( $errno ) {
377 0         0 $self->error( "Failed with error $errno" );
378 0         0 invoke_callback( $cb_final, 'FAIL', errno => $errno );
379             } else {
380 0         0 $self->error( "Unknown failure" );
381 0         0 invoke_callback( $cb_final, 'FAIL' );
382             }
383 0         0 };
384              
385 0         0 my $expires;
386 0         0 $self->{endpoint}->register( %rarg, callback => [ $cb,$self,$cb_final,\$expires ] );
387              
388             # if cb_final is local stopvar wait until it got set
389 0 0       0 if ( \$stopvar == $cb_final ) {
390 0         0 $self->loop( \$stopvar );
391 0 0       0 return $stopvar eq 'OK' ? $expires: undef;
392             }
393             }
394              
395             ###########################################################################
396             # create new call
397             # and waits until the INVITE is completed (e.g final response received)
398             # Args: ($self,$ctx;%args)
399             # $ctx: \%ctx context describing the call or sip address of peer
400             # %args: see Net::SIP::Simple::Call::invite
401             # Returns: $call
402             # $call: Net::SIP::Simple::Call
403             ###########################################################################
404             sub invite {
405 35     35 1 11077 my Net::SIP::Simple $self = shift;
406 35         357 my ($ctx,%args) = @_;
407 35 50       180 (my $to,$ctx) = ref($ctx) ? ($ctx->{to},$ctx) : ($ctx,undef);
408 35 50       140 $to || croak( "need peer of call" );
409 35 50 33     748 if ( $to !~m{\s} && $to !~m{\@} ) {;
410 0 0       0 croak( "no domain and no fully qualified to" ) if ! $self->{domain};
411 0 0       0 my $sip_proto = $self->{dispatcher}->get_legs(proto => 'tls')
412             ? 'sips' : 'sip';
413 0         0 $to = "$to <$sip_proto:$to\@$self->{domain}>";
414 0 0       0 $ctx->{to} = $to if $ctx;
415             }
416 35   33     1380 my $call = Net::SIP::Simple::Call->new( $self,$ctx||$to );
417 35         404 $call->reinvite(%args);
418 35         178 return $call;
419             }
420              
421             ###########################################################################
422             # listen for and accept new calls
423             # Args: ($self,%args)
424             # %args:
425             # filter: optional sub or regex to filter which incoming calls gets accepted
426             # if not given all calls will be accepted
427             # if regex only from matching regex gets accepted
428             # if sub and sub returns 1 call gets accepted, if sub returns 0 it gets rejected
429             # cb_create: optional callback called on creation of newly created
430             # Net::SIP::Simple::Call. If returns false the call will be closed.
431             # If returns a callback (e.g some ref) it will be used instead of
432             # Net::SIP::Simple::Call to handle the data
433             # cb_established: callback called after receiving ACK
434             # cb_cleanup: called on destroy of call object
435             # auth_whatever: will require authorization, see whatever in Net::SIP::Authorize
436             # for all other args see Net::SIP::Simple::Call....
437             # Returns: NONE
438             ###########################################################################
439             sub listen {
440 18     18 1 210 my Net::SIP::Simple $self = shift;
441 18         473 my %args = @_;
442 18         165 my $cb_create = delete $args{cb_create};
443              
444             # handle new requests
445             my $receive = sub {
446 18     18   70 my ($self,$args,$endpoint,$ctx,$request,$leg,$from) = @_;
447 18         88 my $method = $request->method;
448 18 50       257 if ( $method eq 'OPTIONS' ) {
    50          
449 0         0 my $response = $request->create_response( '200','OK',$self->{options} );
450 0         0 $self->{endpoint}->new_response( $ctx,$response,$leg,$from );
451 0         0 $self->{endpoint}->close_context( $ctx );
452 0         0 return;
453             } elsif ( $method ne 'INVITE' ) {
454 0         0 DEBUG( 10,"drop non-INVITE request: ".$request->dump );
455 0         0 $self->{endpoint}->close_context( $ctx );
456 0         0 return;
457             }
458              
459 18 50       124 if ( my $filter = $args->{filter} ) {
460 0         0 my $rv = invoke_callback( $filter, $ctx->{from},$request );
461 0 0       0 if ( !$rv ) {
462 0         0 DEBUG( 1, "call from '$ctx->{from}' rejected" );
463 0         0 $self->{endpoint}->close_context( $ctx );
464 0         0 return;
465             }
466             }
467              
468             # new invite, create call
469 18         635 my $call = Net::SIP::Simple::Call->new( $self,$ctx,{ %$args });
470 18   50     420 my $cb = UNIVERSAL::can( $call,'receive' ) || die;
471              
472             # notify caller about new call
473 18 100       100 if ($cb_create) {
474 12         128 my $cbx = invoke_callback($cb_create, $call, $request, $leg, $from);
475 12 50       7939 if ( ! $cbx ) {
    100          
476 0         0 DEBUG( 1, "call from '$ctx->{from}' rejected in cb_create" );
477 0         0 $self->{endpoint}->close_context( $ctx );
478 0         0 return;
479             } elsif ( ref($cbx) ) {
480 3         14 $cb = $cbx
481             }
482             }
483              
484             # setup callback on context and call it for this packet
485 18         157 $ctx->set_callback([ $cb,$call ]);
486 18         141 $cb->( $call,$endpoint,$ctx,undef,undef,$request,$leg,$from );
487 18         552 };
488              
489 18         319 $self->{endpoint}->set_application( [ $receive,$self,\%args] );
490              
491             # in case listener should provide authorization put Authorizer in between
492 18 100       144 if ( my $auth = _make_auth_from_args($self,\%args) ) {
493 1         11 $self->create_chain([$auth,$self->{endpoint}]);
494             }
495             }
496              
497              
498             ###########################################################################
499             # create authorization if args say so
500             # Args: ($self,$args)
501             # %$args:
502             # auth_user2pass: see user2pass in Net::SIP::Authorize
503             # auth_user2a1: see user2a1 in Net::SIP::Authorize
504             # auth_realm: see realm in Net::SIP::Authorize
505             # auth_.... : see Net::SIP::Authorize
506             # Returns: authorizer if auth_* args given, removes auth_ args from hash
507             ##########################################################################
508             sub _make_auth_from_args {
509 19     19   87 my ($self,$args) = @_;
510              
511             my %auth =
512 19 100       153 map { m{^auth_(.+)} ? ($1 => delete $args->{$_}):() }
  39         392  
513             keys %$args;
514 19         101 my $i_am_proxy = delete $auth{i_am_proxy};
515              
516 19   66     212 return %auth && $self->create_auth(%auth);
517             }
518              
519             ###########################################################################
520             # setup authorization for use in chain
521             # Args: ($self,%args)
522             # %args: see Net::SIP::Authorize
523             # Returns: authorizer object
524             ##########################################################################
525             sub create_auth {
526 1     1 1 3 my ($self,%args) = @_;
527             return Net::SIP::Authorize->new(
528             dispatcher => $self->{dispatcher},
529 1         49 %args,
530             );
531             }
532              
533              
534             ###########################################################################
535             # setup a simple registrar
536             # Args: ($self,%args)
537             # %args:
538             # max_expires: maximum expires time accepted fro registration, default 300
539             # min_expires: minimum expires time accepted, default 30
540             # domains|domain: domain or \@list of domains the registrar is responsable
541             # for. special domain '*' catches all
542             # auth_whatever: will require authorization, see whatever in Net::SIP::Authorize
543             # Returns: $registrar
544             ###########################################################################
545             sub create_registrar {
546 0     0 1 0 my Net::SIP::Simple $self = shift;
547 0         0 my %args = @_;
548 0         0 my $auth = _make_auth_from_args($self,\%args);
549              
550             my $registrar = Net::SIP::Registrar->new(
551             dispatcher => $self->{dispatcher},
552 0         0 %args
553             );
554 0 0       0 if ( $auth ) {
555 0         0 $registrar = $self->create_chain(
556             [$auth,$registrar],
557             methods => ['REGISTER']
558             )
559             } else {
560 0         0 $self->{dispatcher}->set_receiver( $registrar );
561             }
562 0         0 return $registrar;
563             }
564              
565             ###########################################################################
566             # setup a stateless proxy
567             # Args: ($self,%args)
568             # %args: see Net::SIP::StatelessProxy, for auth_whatever see whatever
569             # in Net::SIP::Authorize
570             # Returns: $proxy
571             ###########################################################################
572             sub create_stateless_proxy {
573 1     1 1 5 my Net::SIP::Simple $self = shift;
574 1         2 my %args = @_;
575              
576 1         2 $args{auth_i_am_proxy} = 1;
577 1         4 my $auth = _make_auth_from_args($self,\%args);
578              
579             my $proxy = Net::SIP::StatelessProxy->new(
580             dispatcher => $self->{dispatcher},
581 1         8 %args
582             );
583 1 50       2 if ( $auth ) {
584 0         0 $proxy = $self->create_chain([$auth,$proxy])
585             } else {
586 1         3 $self->{dispatcher}->set_receiver($proxy);
587             }
588 1         2 return $proxy;
589             }
590              
591             ###########################################################################
592             # setup chain of handlers, e.g. first authorize all requests, everything
593             # else gets handled by stateless proxy etc
594             # Args: ($self,$objects,%args)
595             # Returns: $chain
596             ###########################################################################
597             sub create_chain {
598 1     1 1 2 my Net::SIP::Simple $self = shift;
599 1         34 my $chain = Net::SIP::ReceiveChain->new( @_ );
600 1         5 $self->{dispatcher}->set_receiver( $chain );
601 1         2 return $chain;
602             }
603              
604             1;