This file is indexed.

/usr/share/perl5/Net/SIP/Registrar.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
###########################################################################
# package Net::SIP::Registrar
# implements a simple Registrar
# FIXME: store registry information in a more flat format, so that
#  user can give a tied hash for permanent storage. Or give an object
#  interface with a simple default implementation but a way for the
#  user to provide its own implementation
###########################################################################

use strict;
use warnings;

package Net::SIP::Registrar;
use fields qw( store max_expires min_expires dispatcher domains _last_timer );
use Net::SIP::Util ':all';
use Carp 'croak';
use Net::SIP::Debug;
use List::Util 'first';

###########################################################################
# creates new registrar
# Args: ($class,%args)
#   %args
#     max_expires: maximum time for expire, default 300
#     min_expires: manimum time for expire, default 30
#     dispatcher: Net::SIP::Dispatcher object
#     domains: domain or \@list of domains the registrar is responsable
#        for, if not given it cares about everything
#     domain: like domains if only one domain is given
# Returns: $self
###########################################################################
sub new {
	my $class = shift;
	my %args = @_;
	my $domains = delete $args{domains} || delete $args{domain};
	$domains = [ $domains ] if $domains && !ref($domains);

	my $self = fields::new($class);
	%$self = %args;
	$self->{max_expires} ||= 300;
	$self->{min_expires} ||= 30;
	$self->{dispatcher} or croak( "no dispatcher given" );
	$self->{store} = {};
	$self->{domains} = $domains;
	return $self;
}

# hack to have access to the store, to dump or restore it
sub _store {
	my $self = shift;
	$self->{store} = shift if @_;
	return $self->{store};
}

###########################################################################
# handle packet, called from Net::SIP::Dispatcher on incoming requests
# Args: ($self,$packet,$leg,$addr)
#  $packet: Net::SIP::Request
#  $leg: Net::SIP::Leg where request came in (and response gets send out)
#  $addr: ip:port where request came from and response will be send
# Returns: $code
#  $code: response code used in response (usually 200, but can be 423
#    if expires was too small). If not given no response was created
#    and packet was ignored
###########################################################################
sub receive {
	my Net::SIP::Registrar $self = shift;
	my ($packet,$leg,$addr) = @_;

	# accept only REGISTER
	$packet->is_request || return;
	if ( $packet->method ne 'REGISTER' ) {
		# if we know the target rewrite the destination URI
		my $addr = (sip_uri2parts($packet->uri))[3];
		DEBUG( 1,"method ".$packet->method." addr=<$addr>" );
		my @found = $self->query( $addr );
		@found or do {
			DEBUG( 1, "$addr not locally registered" );
			return;
		};
		DEBUG( 1,"rewrite URI $addr in ".$packet->method." to $found[0]" );
		$packet->set_uri( $found[0] );
		return; # propagate to next in chain
	}

	my $to = $packet->get_header( 'to' ) or do {
		DEBUG( 1,"no to in register request. DROP" );
		return;
	};

	# what address will be registered
	($to) = sip_hdrval2parts( to => $to );
	if ( my ($domain,$user,$proto) = sip_uri2parts( $to ) ) {
		# normalize if possible
		$to = "$proto:$user\@$domain";
	}

	# check if domain is allowed
	if ( my $rd = $self->{domains} ) {
		my ($domain) = $to =~m{\@([\w\-\.]+)};
		if ( ! first { $domain =~m{\.?\Q$_\E$}i || $_ eq '*' } @$rd ) {
			DEBUG( 1, "$domain matches none of my own domains. DROP" );
			return;
		}
	}

	my $disp = $self->{dispatcher};
	my $loop = $disp->{eventloop};
	my $now = int($loop->looptime);
	my $glob_expire = $packet->get_header( 'expires' );

	# to which contacs it will be registered
	my @contact = $packet->get_header( 'contact' );

	my %curr;
	foreach my $c (@contact) {
		# update contact info
		my ($c_addr,$param) = sip_hdrval2parts( contact => $c );
		$c_addr = $1 if $c_addr =~m{<(\w+:\S+)>}; # do we really need this?
		my $expire = $param->{expires};
		$expire = $glob_expire if ! defined $expire;
		$expire = $self->{max_expires}
			if ! defined $expire || $expire > $self->{max_expires};
		if ( $expire ) {
			if ( $expire < $self->{min_expires} ) {
				# expire to small
				my $response = $packet->create_response(
					'423','Interval too brief',
				);
				$disp->deliver( $response, leg => $leg, dst_addr => $addr );
				return 423;
			}
			$expire += $now if $expire;
		}
		$curr{$c_addr} = $expire;
	}

	$self->{store}{ $to } = \%curr;

	# expire now!
	$self->expire();
	DEBUG_DUMP( 100,$self->{store} );

	# send back a list of current contacts
	my $response = $packet->create_response( '200','OK' );
	while ( my ($where,$expire) = each %curr ) {
		$expire -= $now;
		$response->add_header( contact => "<$where>;expires=$expire" );
	}

	# send back where it came from
	$disp->deliver( $response, leg => $leg, dst_addr => $addr );
	return 200;
}

###########################################################################
# return information for SIP address
# Args: ($self,$addr)
# Returns: @sip_contacts
###########################################################################
sub query {
	my Net::SIP::Registrar $self = shift;
	my $addr = shift;
	DEBUG( 50,"lookup of $addr" );
	my $contacts = $self->{store}{$addr} || return;
	return grep { m{^sips?:} } keys %$contacts;
}

###########################################################################
# remove all expired entries from store
# Args: $self
# Returns: none
###########################################################################
sub expire {
	my Net::SIP::Registrar $self = shift;

	my $disp = $self->{dispatcher};
	my $loop = $disp->{eventloop};
	my $now = $loop->looptime;

	my $store = $self->{store};
	my (@drop_addr,$next_exp);
	while ( my ($addr,$contact) = each %$store ) {
		my @drop_where;
		while ( my ($where,$expire) = each %$contact ) {
			if ( $expire<$now ) {
				push @drop_where, $where;
			} else {
				$next_exp = $expire if ! $next_exp || $expire < $next_exp;
			}
		}
		if ( @drop_where ) {
			delete @{$contact}{ @drop_where };
			push @drop_addr,$addr if !%$contact;
		}
	}
	delete @{$store}{ @drop_addr } if @drop_addr;

	# add timer for next expire
	if ( $next_exp ) {
		my $last_timer = \$self->{_last_timer};
		if ( ! $$last_timer || $next_exp < $last_timer || $$last_timer <= $now ) {
			$disp->add_timer( $next_exp, [ \&expire, $self ] );
			$$last_timer = $next_exp;
		}
	}
}

1;