blob: 892f5a33df11b556097769cf43c12db2553b4aaa [file] [log] [blame]
/* Component implementing a SIP UA towards Asterisk
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
* (at your option) any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
module SIP_ConnectionHandler {
import from TCCOpenSecurity_Functions all;
import from General_Types all;
import from Osmocom_Types all;
import from Native_Functions all;
import from Misc_Helpers all;
import from SDP_Types all;
import from SDP_Templates all;
import from SIP_Emulation all;
import from SIPmsg_Types all;
import from SIP_Templates all;
type port Coord_PT message
{
inout charstring;
} with { extension "internal" };
const charstring COORD_CMD_REGISTERED := "COORD_CMD_REGISTERED";
const charstring COORD_CMD_START := "COORD_CMD_START";
const charstring COORD_CMD_PICKUP := "COORD_CMD_PICKUP";
const charstring COORD_CMD_CALL_ESTABLISHED := "COORD_CMD_CALL_ESTABLISHED";
const charstring COORD_CMD_CALL_CANCELLED := "COORD_CMD_CALL_CANCELLED";
const charstring COORD_CMD_HANGUP := "COORD_CMD_HANGUP";
type component SIPConnHdlr extends SIP_ConnHdlr {
var charstring g_name;
var SIPConnHdlrPars g_pars;
timer g_Tguard;
var PDU_SIP_Request g_rx_sip_req;
var PDU_SIP_Response g_rx_sip_resp;
port Coord_PT COORD;
}
type record of SIPConnHdlr SIPConnHdlrList;
type record SIPConnHdlrPars {
float t_guard,
charstring remote_sip_host,
uint16_t remote_sip_port,
charstring user,
charstring display_name,
charstring password,
SipUrl registrar_sip_req_uri,
SipAddr registrar_sip_record,
CallidString registrar_sip_call_id,
integer registrar_sip_seq_nr,
Via local_via,
SipUrl local_sip_url_ext,
SipAddr local_sip_record,
Contact local_contact,
Authorization authorization optional,
CallPars cp optional
}
type record of SIPConnHdlrPars SIPConnHdlrParsList;
type record CallParsMT {
/* Whether to wait for COORD.receive(COORD_CMD_PICKUP) before accepting the call. */
boolean wait_coord_cmd_pickup,
/* Whether to expect CANCEL instead of ACK as answer to our OK */
boolean exp_cancel
}
template (value) CallParsMT t_CallParsMT := {
wait_coord_cmd_pickup := false,
exp_cancel := false
}
type record CallPars {
SipAddr calling optional,
SipAddr called optional,
From from_addr optional,
To to_addr optional,
CallidString sip_call_id,
integer sip_seq_nr,
charstring sip_body optional,
charstring local_rtp_addr,
uint16_t local_rtp_port,
/* Whether to expect Asterisk to re-INVITE to make RTP flow directly to peer. */
boolean exp_update_to_direct_rtp,
SDP_Message peer_sdp optional,
CallParsMT mt
}
template (value) CallPars t_CallPars(charstring local_rtp_addr,
uint16_t local_rtp_port := 0,
template (omit) SipAddr calling := omit,
template (omit) SipAddr called := omit) := {
calling := calling,
called := called,
from_addr := omit,
to_addr := omit,
sip_call_id := hex2str(f_rnd_hexstring(15)),
sip_seq_nr := f_sip_rand_seq_nr(),
sip_body := omit,
local_rtp_addr := local_rtp_addr,
local_rtp_port := local_rtp_port,
exp_update_to_direct_rtp := true,
peer_sdp := omit,
mt := t_CallParsMT
}
template (value) SIPConnHdlrPars t_Pars(charstring local_sip_host,
uint16_t local_sip_port,
charstring remote_sip_host,
uint16_t remote_sip_port,
charstring user,
charstring display_name := "Anonymous",
charstring password := "secret",
template (omit) CallPars cp := omit) := {
t_guard := 30.0,
remote_sip_host := remote_sip_host,
remote_sip_port := remote_sip_port,
user := user,
display_name := f_sip_str_quote(display_name),
password := password,
registrar_sip_req_uri := valueof(ts_SipUrlHost(remote_sip_host)),
registrar_sip_record := ts_SipAddr(ts_HostPort(remote_sip_host),
ts_UserInfo(user),
f_sip_str_quote(display_name)),
registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
registrar_sip_seq_nr := f_sip_rand_seq_nr(),
local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
ts_UserInfo(user)),
local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
ts_UserInfo(user)),
local_contact := valueof(ts_Contact({
ts_ContactAddress(
ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
local_sip_host,
local_sip_port),
ts_UserInfo(user))),
omit)
})),
authorization := omit,
cp := cp
}
private altstep as_Tguard() runs on SIPConnHdlr {
[] g_Tguard.timeout {
setverdict(fail, "Tguard timeout");
mtc.stop;
}
}
type function void_fn(charstring id) runs on SIPConnHdlr;
function f_handler_init(void_fn fn, charstring id, SIPConnHdlrPars pars)
runs on SIPConnHdlr {
g_name := id;
g_pars := pars;
g_Tguard.start(pars.t_guard);
activate(as_Tguard());
// Make sure the UA is deregistered before starting the test:
// sends REGISTER with Contact = "*" and Expires = 0
//f_SIP_deregister();
/* call the user-supied test case function */
fn.apply(id);
}
private function f_tr_Via_response(Via via_req) return template (present) Via {
template (present) SemicolonParam_List via_resp_params := ?;
/*via_resp_params := {
{ id := "rport", paramValue := int2str(g_pars.remote_sip_port) },
{ id := "received", paramValue := g_pars.remote_sip_host }
}; */
return tr_Via_from(via_req.viaBody[0].sentBy,
via_req.viaBody[0].sentProtocol.transport,
via_resp_params);
}
private function f_tr_To_response(template (value) SipAddr to_req) return template (present) SipAddr {
return tr_SipAddr_from_val(to_req);
}
private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
return tr_SipAddr_from_val(from_req);
}
private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on SIPConnHdlr
{
var PDU_SIP_Request sip_req;
[] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
}
}
private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on SIPConnHdlr
{
var PDU_SIP_Response sip_resp;
[] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
}
}
altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on SIPConnHdlr
{
var charstring sip_expect_str := log2str(sip_expect);
[] SIP.receive(sip_expect) -> value g_rx_sip_req;
[fail_others] as_SIP_fail_req(sip_expect_str);
[fail_others] as_SIP_fail_resp(sip_expect_str);
}
altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on SIPConnHdlr
{
var charstring sip_expect_str := log2str(sip_expect);
[] SIP.receive(sip_expect) -> value g_rx_sip_resp;
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on SIPConnHdlr
{
[] SIP.receive(sip_expect) -> value g_rx_sip_resp {
log("Ignoring ", g_rx_sip_resp);
repeat;
}
}
private function f_gen_sdp() runs on SIPConnHdlr return charstring {
var charstring sdp :=
"v=0\r\n" &
"o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
"s=Talk\r\n" &
"c=IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
"t=0 0\r\n" &
"a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
"a=record:off\r\n" &
"m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" &
"a=rtpmap:8 PCMA/8000\r\n" &
"a=rtpmap:96 opus/48000/2\r\n" &
"a=fmtp:96 useinbandfec=1\r\n" &
"a=rtpmap:97 speex/16000\r\n" &
"a=fmtp:97 vbr=on\r\n" &
"a=rtpmap:98 speex/8000\r\n" &
"a=fmtp:98 vbr=on\r\n" &
"a=fmtp:18 annexb=yes\r\n" &
"a=rtpmap:99 telephone-event/48000\r\n" &
"a=rtpmap:100 telephone-event/16000\r\n" &
"a=rtpmap:101 telephone-event/8000\r\n" &
"a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" &
"a=rtcp-fb:* trr-int 1000\r\n" &
"a=rtcp-fb:* ccm tmmbr\r\n";
return sdp;
}
function f_SIP_register() runs on SIPConnHdlr return PDU_SIP_Response
{
var template (present) PDU_SIP_Response exp;
var Authorization authorization;
var Via via := g_pars.local_via;
var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
var template (present) To to_addr_exp := tr_To(tr_Addr_Union_from_val(to_addr.addressField), *);
var charstring branch_value;
branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
g_pars.registrar_sip_call_id,
g_pars.registrar_sip_seq_nr);
via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
g_pars.registrar_sip_call_id,
from_addr,
to_addr,
via,
g_pars.registrar_sip_seq_nr,
g_pars.local_contact,
ts_Expires("7200")));
exp := tr_SIP_Response_Unauthorized(
g_pars.registrar_sip_call_id,
from_addr,
to_addr_exp,
f_tr_Via_response(via),
*,
tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
g_pars.registrar_sip_seq_nr);
as_SIP_expect_resp(exp);
/* Digest Auth: RFC 2617 */
g_pars.authorization :=
f_sip_digest_gen_Authorization_MD5(g_rx_sip_resp.msgHeader.wwwAuthenticate,
g_pars.user, g_pars.password,
"REGISTER",
f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
/* New transaction: */
g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
g_pars.registrar_sip_call_id,
g_pars.registrar_sip_seq_nr);
via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
g_pars.registrar_sip_call_id,
from_addr,
to_addr,
via,
g_pars.registrar_sip_seq_nr,
g_pars.local_contact,
ts_Expires("7200"),
authorization := g_pars.authorization));
/* Wait for OK answer */
exp := tr_SIP_Response(
g_pars.registrar_sip_call_id,
from_addr,
to_addr_exp,
f_tr_Via_response(via),
*,
"REGISTER", 200,
g_pars.registrar_sip_seq_nr, "OK");
as_SIP_expect_resp(exp);
/* Prepare for next use: */
g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
return g_rx_sip_resp;
}
function f_SIP_unregister() runs on SIPConnHdlr return PDU_SIP_Response
{
var template (present) PDU_SIP_Response exp;
var Via via := g_pars.local_via;
var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
var template (present) To to_addr_exp := tr_To(tr_Addr_Union_from_val(to_addr.addressField), *);
var charstring branch_value;
branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
g_pars.registrar_sip_call_id,
g_pars.registrar_sip_seq_nr);
via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
g_pars.registrar_sip_call_id,
from_addr,
to_addr,
via,
g_pars.registrar_sip_seq_nr,
g_pars.local_contact,
ts_Expires("0"),
authorization := g_pars.authorization));
/* Wait for OK answer */
exp := tr_SIP_Response(
g_pars.registrar_sip_call_id,
from_addr,
to_addr_exp,
f_tr_Via_response(via),
*,
"REGISTER", 200,
g_pars.registrar_sip_seq_nr, "OK");
as_SIP_expect_resp(exp);
/* Prepare for next use: */
g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
return g_rx_sip_resp;
}
function f_SIP_mo_call_setup() runs on SIPConnHdlr
{
var template (value) PDU_SIP_Request req;
var template (present) PDU_SIP_Response exp;
var template (present) From from_addr_exp;
var template (present) To to_addr_exp;
var Via via;
var charstring tx_sdp := f_gen_sdp();
var default d_trying, d_ringing;
var charstring branch_value;
/* RFC 3261 8.1.1.3 From */
g_pars.cp.from_addr := valueof(ts_From(g_pars.cp.calling.addr, g_pars.cp.calling.params));
g_pars.cp.from_addr.fromParams := f_sip_param_set(g_pars.cp.from_addr.fromParams, "tag", f_sip_rand_tag());
g_pars.cp.to_addr := valueof(ts_To(g_pars.cp.called.addr, g_pars.cp.called.params));
from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.cp.from_addr.addressField), *);
to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *);
branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
g_pars.cp.sip_call_id,
g_pars.cp.sip_seq_nr);
via := g_pars.local_via;
via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
req := ts_SIP_INVITE(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
via,
g_pars.local_contact,
g_pars.cp.sip_seq_nr,
body := tx_sdp);
SIP.send(req);
/* RFC 3261 22.2: */
exp := tr_SIP_Response_Unauthorized(
g_pars.cp.sip_call_id,
from_addr_exp,
to_addr_exp,
f_tr_Via_response(via),
*,
tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
g_pars.cp.sip_seq_nr, "INVITE");
as_SIP_expect_resp(exp);
/* Digest Auth: RFC 2617 */
req.msgHeader.authorization := f_sip_digest_gen_Authorization_MD5(
g_rx_sip_resp.msgHeader.wwwAuthenticate,
g_pars.user, g_pars.password,
"INVITE",
f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
f_sip_Request_inc_seq_nr(req);
SIP.send(req);
/* Conditionally match and accept 100 Trying. */
exp := tr_SIP_Response_Trying(g_pars.cp.sip_call_id,
from_addr_exp,
to_addr_exp,
f_tr_Via_response(via),
g_pars.cp.sip_seq_nr, "INVITE");
d_trying := activate(as_SIP_ignore_resp(exp));
/* Conditionally match and accept 180 Ringing */
exp := tr_SIP_Response_Ringing(g_pars.cp.sip_call_id,
from_addr_exp,
to_addr_exp,
f_tr_Via_response(via),
g_pars.cp.sip_seq_nr, "INVITE");
d_ringing := activate(as_SIP_ignore_resp(exp));
/* Wait for OK answer */
exp := tr_SIP_Response(
g_pars.cp.sip_call_id,
from_addr_exp,
to_addr_exp,
f_tr_Via_response(via),
*,
"INVITE", 200,
g_pars.cp.sip_seq_nr, "OK",
body := ?);
as_SIP_expect_resp(exp, fail_others := false);
deactivate(d_trying);
deactivate(d_ringing);
/* Update To with the tags received from peer: */
g_pars.cp.to_addr := g_rx_sip_resp.msgHeader.toField;
/* Transmit ACK */
g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
req := ts_SIP_ACK(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
via,
g_pars.cp.sip_seq_nr,
omit);
SIP.send(req);
g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
}
private function f_ConnHdlr_parse_initial_SIP_INVITE(PDU_SIP_Request rx_sip_req) runs on SIPConnHdlr
{
f_SDP_decodeMessage(rx_sip_req.messageBody, g_pars.cp.peer_sdp);
log("Rx Initial MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
/* Obtain params: */
g_pars.cp.sip_call_id := rx_sip_req.msgHeader.callId.callid;
g_pars.cp.from_addr := rx_sip_req.msgHeader.fromField;
g_pars.cp.to_addr := rx_sip_req.msgHeader.toField;
g_pars.cp.to_addr.toParams := f_sip_param_set(g_pars.cp.to_addr.toParams, "tag", f_sip_rand_tag());
g_pars.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
}
/* Peer is calling us, accept it: */
altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true,
boolean fail_others := true) runs on SIPConnHdlr
{
var template (present) PDU_SIP_Request exp_req :=
tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
?,
tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
?, ?);
var charstring sip_expect_str := log2str(exp_req);
[] SIP.receive(exp_req) -> value g_rx_sip_req {
var template (value) PDU_SIP_Response tx_resp;
var Via via;
var charstring tx_sdp;
/* Obtain params: */
f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
via := g_rx_sip_req.msgHeader.via;
/* Tx 180 Ringing */
tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
via,
g_pars.cp.sip_seq_nr);
SIP.send(tx_resp);
if (g_pars.cp.mt.wait_coord_cmd_pickup) {
COORD.receive(COORD_CMD_PICKUP);
}
/* Tx 200 OK */
tx_sdp := f_gen_sdp();
tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
"INVITE", 200,
g_pars.cp.sip_seq_nr,
"OK",
via,
body := tx_sdp);
SIP.send(tx_resp);
/* Wait for ACK */
exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
f_tr_Via_response(via),
g_pars.cp.sip_seq_nr, *);
as_SIP_expect_req(exp_req);
if (exp_update_to_direct_rtp) {
/* Asterisk will now update the session to connect us to MO directly: */
/* Via is not kept since anyway "branch" will change upon following INVITE. */
as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
}
}
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
/* Peer is calling us, but cancells it during ringing: */
altstep as_SIP_mt_call_cancelled(boolean fail_others := true) runs on SIPConnHdlr
{
var template (present) PDU_SIP_Request exp_req :=
tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
?,
tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
?, ?);
var charstring sip_expect_str := log2str(exp_req);
[] SIP.receive(exp_req) -> value g_rx_sip_req {
var template (value) PDU_SIP_Response tx_resp;
var Via via;
var template (present) To exp_to_addr;
var charstring tx_sdp;
/* Obtain params: */
f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
via := g_rx_sip_req.msgHeader.via;
/* Tx 180 Ringing */
tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
via,
g_pars.cp.sip_seq_nr);
SIP.send(tx_resp);
if (g_pars.cp.mt.wait_coord_cmd_pickup) {
COORD.receive(COORD_CMD_PICKUP);
}
/* Wait for CANCEL */
/* Cancel may come even before we send Ringing, hence To's "tag"
* may not be known by peer, so g_pars.to_addr can't be used here: */
exp_to_addr := g_rx_sip_req.msgHeader.toField;
exp_req := tr_SIP_CANCEL(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
exp_to_addr,
f_tr_Via_response(via),
g_pars.cp.sip_seq_nr, *);
as_SIP_expect_req(exp_req);
/* Tx 200 OK */
tx_sdp := f_gen_sdp();
tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
"CANCEL", 200,
g_pars.cp.sip_seq_nr,
"OK",
via,
body := omit);
SIP.send(tx_resp);
}
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
/* New INVITE arrives after MT call is established. Accept it: */
altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
{
var template (present) PDU_SIP_Request exp_req :=
tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
exp_seq_nr,
?);
var charstring sip_expect_str := log2str(exp_req);
[] SIP.receive(exp_req) -> value g_rx_sip_req {
var template (value) PDU_SIP_Response tx_resp;
var charstring tx_sdp;
var Via via;
f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
log("Rx Update MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
/* Update parameters: */
g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
/* "branch" has changed: */
via := g_rx_sip_req.msgHeader.via;
/* Tx 200 OK */
tx_sdp := f_gen_sdp();
tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
"INVITE", 200,
g_pars.cp.sip_seq_nr,
"OK",
via,
body := tx_sdp);
SIP.send(tx_resp);
/* Wait for ACK */
exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
f_tr_Via_response(via),
g_pars.cp.sip_seq_nr, *);
as_SIP_expect_req(exp_req);
}
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
/* Tx BYE: */
function f_SIP_do_call_hangup() runs on SIPConnHdlr
{
var template (value) PDU_SIP_Request req;
var template (present) PDU_SIP_Response exp_resp;
var Via via;
var charstring branch_value;
branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
g_pars.cp.sip_call_id,
g_pars.cp.sip_seq_nr);
via := g_pars.local_via;
via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
/* Transmit ACK */
req := ts_SIP_BYE(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
via,
g_pars.cp.sip_seq_nr,
omit);
SIP.send(req);
/* Wait for OK answer */
exp_resp := tr_SIP_Response(
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *),
f_tr_Via_response(via),
*,
"BYE", 200,
g_pars.cp.sip_seq_nr, "OK");
as_SIP_expect_resp(exp_resp);
g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
}
/* Call is terminated by peer: */
altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
{
var template (present) PDU_SIP_Request exp_req :=
tr_SIP_BYE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
exp_seq_nr);
var charstring sip_expect_str := log2str(exp_req);
[] SIP.receive(exp_req) -> value g_rx_sip_req {
var template (value) PDU_SIP_Response tx_resp;
var charstring tx_sdp;
var Via via;
/* Update parameters: */
g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
/* "branch" has changed: */
via := g_rx_sip_req.msgHeader.via;
/* Tx 200 OK */
tx_sdp := f_gen_sdp();
tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
"BYE", 200,
g_pars.cp.sip_seq_nr,
"OK",
via,
body := tx_sdp);
SIP.send(tx_resp);
}
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
}