/usr/share/perl5/Net/SIP/Endpoint.pm is in libnet-sip-perl 0.66-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | ############################################################################
# package Net::SIP::Endpoint
# implements the behavior of an endpoint (SIP phone).
# packet managment (lower layer) is done by Net::SIP::Dispatcher while
# call managment is done with Net::SIP::Endpoint::Context
############################################################################
use strict;
use warnings;
package Net::SIP::Endpoint;
use fields (
'dispatcher', # lower layer, delivers and receives packets through the legs
'application', # upper layer, e.g user interface..
'ctx' # hash of ( callid => Net::SIP::Endpoint::Context )
);
use Net::SIP::Debug;
use Net::SIP::Endpoint::Context;
use Net::SIP::Util qw(invoke_callback);
use Scalar::Util 'weaken';
############################################################################
# create a new endpoint
# Args: ($class,$dispatcher)
# $dispatcher: lower layer which handles the delivery and receiving of packets
# Returns: $self
############################################################################
sub new {
my ($class,$dispatcher) = @_;
my $self = fields::new($class);
$self->{dispatcher} = $dispatcher;
$self->{ctx} = {}; # \%hash with ( callid => $ctx )
# announce myself as upper layer for incoming packets to
# the dispatcher
my $cb = [ \&receive,$self ];
weaken( $cb->[1] );
$dispatcher->set_receiver( $cb );
return $self;
}
############################################################################
# set upper layer (application)
# Args: ($self,$app)
# $app: upper layer which needs to have method receive( $request )
# to handle new request, which this layer cannot handle alone
# (e.g INVITE to a new dialog)
# or this can be \&sub, [ \&sub,@arg ]...
# Returns: NONE
############################################################################
sub set_application {
my Net::SIP::Endpoint $self = shift;
my $app = shift;
my $cb;
if ( my $sub = UNIVERSAL::can( $app,'receive' )) {
$cb = [ $sub,$app ];
} else {
$cb = $app; # alreday callback
}
$self->{application} = $cb;
}
############################################################################
# create a new call or re-invite on a existing call
# wrapper around new_request()
# Args: ($self,$ctx;$callback,$body,%args)
# $ctx: Context|\%args, see new_request()
# $callback: optional Callback, see new_request()
# $body: optional Body
# %args: additional args for Net::SIP::Request::new
# Returns: $ctx
# $ctx: see new_request()
############################################################################
sub invite {
my Net::SIP::Endpoint $self = shift;
my ($ctx,$callback,$body,%args) = @_;
return $self->new_request( 'INVITE',$ctx,$callback,$body,%args );
}
############################################################################
# registers UAC
# Args: ($self,%args)
# %args: at minimum there must be
# from: the sip-address to register
# contact: to which local address should it registered
# registrar: where it should be registered
# there can be:
# expires: Expires header, defaults to 900 if not given
# callback: callback which will be called on response
# callid: callid used for calling context
# all other args will be used in creation of request
# Returns: NONE
############################################################################
sub register {
my Net::SIP::Endpoint $self = shift;
my %args = @_;
my ($me,$registrar,$contact) =
delete @args{qw( from registrar contact )};
my $expires = delete $args{expires};
$expires = 900 if !defined($expires);
my %ctx = (
to => $me,
from => $me,
contact => $contact,
auth => delete $args{auth},
callid => delete $args{callid},
);
return $self->new_request(
'REGISTER',
\%ctx,
delete($args{callback}),
undef,
uri => "sip:$registrar",
expires => $expires,
%args,
);
}
############################################################################
# starts new request, e.g creates request packet and delivers it
# Args: ($self,$method,$ctx;$callback,$body,%args)
# $method: method name, e.g. 'INVITE','REGISTER',..
# can also be a full Net::SIP::Request already (used for retries after
# 302,305 responses)
# $ctx: already established context (Net::SIP::Endpoint::Context)
# or \%hash to create a new one (see Net::SIP::Endpoint::Context->new)
# $callback: [ \&sub,@arg ] which will be called if the layer receives
# responses important to the upper layer (e.g 180 Ringing, 200 Ok,
# 401/407 Authorization required...)
# if callback is ommitted the callback from the context is used,
# if callback is set it will be the new callback for the context
# $body: optional Body, either scalar or smth with method as_string
# (like Net::SIP::SDP)
# %args: additional args for Net::SIP::Endpoint::Context::new_request
# Returns: $ctx
# $ctx: context, eg the original one or newly created
# Comment: if it cannot create a new context (because of missing args)
# or something else fatal happens it will die()
############################################################################
sub new_request {
my Net::SIP::Endpoint $self = shift;
my ($method,$ctx,$callback,$body,%args) = @_;
die "cannot redefine call-id" if delete $args{ 'call-id' };
my ($leg,$dst_addr) = delete @args{qw(leg dst_addr)};
if ( ! UNIVERSAL::isa( $ctx,'Net::SIP::Endpoint::Context' )) {
$ctx = Net::SIP::Endpoint::Context->new(%$ctx, method => $method);
$self->{ctx}{ $ctx->callid } = $ctx; # make sure we manage the context
DEBUG( 10,"create new request for $method within new call ".$ctx->callid );
} else {
DEBUG( 10,"create new request for $method within existing call ".$ctx->callid );
}
$ctx->set_callback( $callback ) if $callback;
my $request = $ctx->new_request( $method,$body,%args );
DEBUG( 50,"request=".$request->as_string );
my $tid = $request->tid;
$self->{dispatcher}->deliver( $request,
id => $tid,
callback => [ \&_request_delivery_callback, $self,$ctx ],
leg => $leg,
dst_addr => $dst_addr,
);
return $ctx;
}
############################################################################
# Cancel last pending INVITE request
# Args: ($self,$ctx,$request,$cb)
# $ctx: context for call
# $request: request to cancel, will only cancel it, if request is
# outstanding in context, will cancel latest INVITE if not given
# $cb: callback for generated CANCEL request
# Returns: number of requests canceled (e.g 0 if no outstanding INVITE)
############################################################################
sub cancel_invite {
my Net::SIP::Endpoint $self = shift;
my Net::SIP::Endpoint::Context $ctx = shift;
my ($request,$callback) = @_;
my ($pkt) = $ctx->find_outstanding_requests(
$request ? ( request => $request ) : ( method => 'INVITE' )
) or return;
$self->new_request( $pkt->create_cancel, $ctx, $callback );
return 1;
}
############################################################################
# internal callback used for delivery
# will be called from dispatcher if the request was definitly successfully
# delivered (tcp only) or an error occurred
# Args: ($self,$ctx,$error,$delivery_packet)
# $ctx: Net::SIP::Endpoint::Context
# $error: errno if error occured
# $delivery_packet: Net::SIP::Dispatcher::Packet which encapsulates
# the original request and information about leg, dst_addr...
# and has method use_next_dstaddr to try the next dstaddr if for the
# current no (more) retries are possible
# Returns: NONE
############################################################################
sub _request_delivery_callback {
my Net::SIP::Endpoint $self = shift;
my ($ctx,$error,$delivery_packet) = @_;
my $tid = $delivery_packet->tid;
# either successfully send over reliable transport
# or permanently failed, e.g no (more) retries possible
$ctx->request_delivery_done( $self,$tid,$error )
}
############################################################################
# remove context from Endpoint and cancel all outstanding deliveries
# Args: ($self,$id)
# $id: either id for ctx or context object or SIP packet
# Returns: $ctx
# $ctx: removed context object
############################################################################
sub close_context {
my Net::SIP::Endpoint $self = shift;
my $id = shift;
$id = $id->callid if ref($id);
DEBUG( 10,"close context call-id $id " );
my $ctx = delete $self->{ctx}{$id} || do {
DEBUG( 50,"no context for call-id $id found" );
return;
};
# cancel all outstanding deliveries
$self->{dispatcher}->cancel_delivery( callid => $id );
return $ctx;
}
############################################################################
# receive packet from dispatcher and forwards it to receive_response
# or receive_request depending on type of packet
# Args: ($self,$packet,$leg,$from)
# $packet: Net::SIP::Packet
# $leg: Net::SIP::Leg through which the packets was received
# $from: ip:port where it got packet from
# Returns: NONE
############################################################################
sub receive {
my Net::SIP::Endpoint $self = shift || return;
my ($packet,$leg,$from) = @_;
return $packet->is_response
? $self->receive_response( $packet,$leg,$from )
: $self->receive_request( $packet,$leg,$from )
;
}
############################################################################
# Handle incoming response packet
# Args: ($self,$response,$leg,$from)
# $response: incoming Net::SIP::Response packet
# $leg: where response came in
# $from: ip:port where it got response from
# Returns: NONE
############################################################################
sub receive_response {
my Net::SIP::Endpoint $self = shift;
my ($response,$leg,$from) = @_;
# find context for response or drop
my $callid = $response->get_header( 'call-id' );
my $ctx = $self->{ctx}{$callid} || do {
DEBUG( 50,"cannot find context for packet with callid=$callid. DROP");
return;
};
DEBUG( 10,"received reply for tid=".$response->tid );
$self->{dispatcher}->cancel_delivery( $response->tid );
$ctx->handle_response( $response,$leg,$from,$self );
}
############################################################################
# Handle incoming request packet
# Args: ($self,$request,$leg,$from)
# $request: incoming Net::SIP::Request packet
# $leg: where response came in
# $from: ip:port where it got response from
# Returns: NONE
############################################################################
sub receive_request {
my Net::SIP::Endpoint $self = shift;
my ($request,$leg,$from) = @_;
# this might be a request for an existing context or for a new context
my $callid = $request->get_header( 'call-id' );
my $ctx = $self->{ctx}{$callid};
my $method = $request->method;
if ( ! $ctx ) {
if ( $method eq 'BYE' || $method eq 'CANCEL' ) {
# no context for this call, reply with 481 call does not exist
# (RFC3261 15.1.2)
$self->new_response(
undef,
$request->create_response( 481,'call does not exist' ),
$leg, # send back thru same leg
$from, # and back to the sender
);
return;
} elsif ( $method eq 'ACK' ) {
# call not exists (maybe closed because of CANCEL)
DEBUG(99,'ignoring ACK for non-existing call');
return;
}
# create a new context;
$ctx = Net::SIP::Endpoint::Context->new(
incoming => 1,
method => $method,
from => scalar( $request->get_header( 'from' )),
to => scalar( $request->get_header( 'to' )),
remote_contact => scalar( $request->get_header( 'contact' )),
callid => scalar( $request->get_header( 'call-id' )),
via => [ $request->get_header( 'via' ) ],
);
$ctx->set_callback( sub {
my ($self,$ctx,undef,undef,$request,$leg,$from) = @_;
invoke_callback( $self->{application}, $self,$ctx,$request,$leg,$from );
});
}
# if I got an ACK cancel delivery of Response to INVITE
if ( $method eq 'ACK' ) {
$self->{dispatcher}->cancel_delivery( $request->tid );
}
$ctx->handle_request( $request,$leg,$from,$self );
}
############################################################################
# deliver a response packet
# Args: ($self,$ctx,$response,$leg,$addr)
# $ctx : Net::SIP::Endpoint::Context which generated response
# $response: Net::SIP::Response packet
# $leg : leg to send out response, eg where the request came in
# $addr : where to send respone (ip:port), eg where the request came from
# Returns: NONE
############################################################################
sub new_response {
my Net::SIP::Endpoint $self = shift;
my ($ctx,$response,$leg,$addr) = @_;
$self->{ctx}{ $ctx->callid } = $ctx if $ctx; # keep context
$self->{dispatcher}->deliver( $response,
leg => $leg,
dst_addr => $addr,
);
}
1;
|