Subject: | StatelessProxy CANCEL gets rejected due to different branch |
Date: | Fri, 31 Mar 2017 13:45:58 +0000 |
To: | "bug-Net-SIP [...] rt.cpan.org" <bug-Net-SIP [...] rt.cpan.org> |
From: | Richard Carver <richard.carver [...] cloudmont.co.uk> |
According to RFC3261 section 9.1 an INVITE followed by a CANCEL requires the branch to be the same in the top Via header. I have a scenario with two issues contributing to the Leg.pm via_branch subroutine calculating a different branch for the CANCEL, resulting in the CANCEL being rejected by the destination server.
1. The source system that I am integrating with uses the same branch for both requests, but adds an additional bespoke parameter to the Via header that is different for both requests. As the whole via header is used in the calculation this results in different branch strings.
2. The INVITE and CANCEL are using proxy authentication. Although they use the same nonce the 'nc' value is incremented for the CANCEL and so are some of the other parameters, resulting in different authentication headers. As the whole via header is used in the calculation this results in different branch strings.
My proposal is to modify Leg.pm via_branch as follows.
1. Use only the branch part of the top Via header when computing the hash, falling back to the whole header if there is no branch specified.
2. Use only part of the authorisation header that we know should be identical over both messages. I am not an expert on authorisation headers, so I expect what I have proposed below will need some tweaking to be valid in more scenarios than my own. I did consider whether the authorisation headers are required at all in this calculation, and I guessed that the reason is for broken clients that do not increment cseq when they send the INVITE after a 407 response. If this guess is correct then perhaps only the nonce is required in the calculation.
sub via_branch {
my Net::SIP::Leg $self = shift;
my ($packet,$level) = @_;
my $val = $self->{branch};
$val .= substr( md5_hex( $packet->tid ),0,15 ) if $level>1;
$val .= substr( md5_hex(
map { grep { m{(?:^|\s)(?:username|realm|nonce|uri).*=} } split(/,/,$_) } (sort $packet->get_header( 'proxy-authorization' ),sort $packet->get_header('proxy-require' )),
$packet->get_header( 'route' ),
$packet->get_header( 'to' ),
$packet->get_header( 'from' ),
(($packet->get_header( 'via' ))[0] || '') =~ m{;branch=([^;]*)} ? $1 : (($packet->get_header( 'via' ))[0] || ''),
($packet->as_parts())[1],
),0,15 ) if $level>2;
return $val;
}