aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2024-04-18 13:47:07 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2024-04-18 19:54:42 +0200
commitcaf028d6437d35786c324c12b10944a0277f99df (patch)
treef4f96fffd7892f92e90145c4f5e03bb0b432c9ea
parent54b614ad5d44c7c607e8e952b3596e4928e7c560 (diff)
asterisk: Split SIPConnHdlr to its own file
-rw-r--r--asterisk/Asterisk_Tests.ttcn716
-rw-r--r--asterisk/SIP_ConnectionHandler.ttcn707
2 files changed, 732 insertions, 691 deletions
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn
index c883bac5..b164ee52 100644
--- a/asterisk/Asterisk_Tests.ttcn
+++ b/asterisk/Asterisk_Tests.ttcn
@@ -26,6 +26,8 @@ import from SIP_Emulation all;
import from SIPmsg_Types all;
import from SIP_Templates all;
+import from SIP_ConnectionHandler all;
+
modulepar {
charstring mp_local_sip_host := "127.0.0.2";
integer mp_local_sip_port := 5060;
@@ -37,135 +39,25 @@ modulepar {
charstring mp_ami_secret := "1234";
}
-type port Coord_PT message
-{
- inout charstring;
-} with { extension "internal" };
-private const charstring COORD_CMD_REGISTERED := "COORD_CMD_REGISTERED";
-private const charstring COORD_CMD_START := "COORD_CMD_START";
-private const charstring COORD_CMD_PICKUP := "COORD_CMD_PICKUP";
-private const charstring COORD_CMD_CALL_ESTABLISHED := "COORD_CMD_CALL_ESTABLISHED";
-private const charstring COORD_CMD_CALL_CANCELLED := "COORD_CMD_CALL_CANCELLED";
-private const charstring COORD_CMD_HANGUP := "COORD_CMD_HANGUP";
-
type component test_CT {
var SIP_Emulation_CT vc_SIP;
port TELNETasp_PT AMI;
port Coord_PT COORD;
}
-type component ConnHdlr extends SIP_ConnHdlr {
- var charstring g_name;
- var ConnHdlrPars 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 ConnHdlr ConnHdlrList;
-
const charstring broadcast_sip_extension := "0500";
-type record ConnHdlrPars {
- float t_guard,
- 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,
- CallPars cp optional
-}
-type record of ConnHdlrPars ConnHdlrParsList;
-
-template (value) ConnHdlrPars t_Pars(charstring user,
- charstring display_name := "Anonymous",
- charstring password := "secret",
- template (value) CallPars cp := t_CallPars()) := {
- t_guard := 30.0,
- user := user,
- display_name := f_sip_str_quote(display_name),
- password := password,
- registrar_sip_req_uri := valueof(ts_SipUrlHost(mp_remote_sip_host)),
- registrar_sip_record := ts_SipAddr(ts_HostPort(mp_remote_sip_host),
- ts_UserInfo(user),
- f_sip_str_quote(display_name)),
- registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & mp_local_sip_host,
- registrar_sip_seq_nr := f_sip_rand_seq_nr(),
- local_via := ts_Via_from(ts_HostPort(mp_local_sip_host, mp_local_sip_port)),
- local_sip_url_ext := ts_SipUrl(ts_HostPort(mp_local_sip_host, mp_local_sip_port),
- ts_UserInfo(user)),
- local_sip_record := ts_SipAddr(ts_HostPort(mp_local_sip_host),
- ts_UserInfo(user)),
- local_contact := valueof(ts_Contact({
- ts_ContactAddress(
- ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
- mp_local_sip_host,
- mp_local_sip_port),
- ts_UserInfo(user))),
- omit)
- })),
- cp := cp
-}
-
-function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return ConnHdlrPars {
- var template (value) CallPars cp := t_CallPars(idx := idx);
- var template (value) ConnHdlrPars pars := t_Pars("0" & int2str(str2int(broadcast_sip_extension) + idx),
- cp := cp);
+function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return SIPConnHdlrPars {
+ var template (value) CallPars cp := t_CallPars(mp_local_sip_host, 1234 + 2*idx);
+ var template (value) SIPConnHdlrPars pars := t_Pars(mp_local_sip_host,
+ mp_local_sip_port,
+ mp_remote_sip_host,
+ mp_remote_sip_port,
+ "0" & int2str(str2int(broadcast_sip_extension) + idx),
+ cp := cp);
return valueof(pars);
}
-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
-}
-private template (value) CallParsMT t_CallParsMT := {
- wait_coord_cmd_pickup := false,
- exp_cancel := false
-}
-
-type record CallPars {
- SipAddr calling optional,
- SipAddr called optional,
-
- SipAddr from_addr optional,
- SipAddr to_addr optional,
-
- CallidString sip_call_id,
- integer sip_seq_nr,
- charstring sip_body optional,
-
- charstring local_rtp_addr,
- uint16_t local_rtp_port,
-
- SDP_Message peer_sdp optional,
- CallParsMT mt
-}
-
-private template (value) CallPars t_CallPars(integer idx := 1,
- 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 := mp_local_sip_host,
- local_rtp_port := 1234 + 2*idx,
- peer_sdp := omit,
- mt := t_CallParsMT
-}
-
/* Initialize connection towards Asterisk AMI */
private function f_init_ami() runs on test_CT {
map(self:AMI, system:AMI);
@@ -178,14 +70,12 @@ function f_init() runs on test_CT {
log("end of f_init");
}
-type function void_fn(charstring id) runs on ConnHdlr;
-
-function f_start_handler(void_fn fn, ConnHdlrPars pars)
-runs on test_CT return ConnHdlr {
- var ConnHdlr vc_conn;
+function f_start_handler(void_fn fn, SIPConnHdlrPars pars)
+runs on test_CT return SIPConnHdlr {
+ var SIPConnHdlr vc_conn;
var charstring id := testcasename() & "-ConnHdlr-" & pars.user;
- vc_conn := ConnHdlr.create(id) alive;
+ vc_conn := SIPConnHdlr.create(id) alive;
connect(vc_conn:SIP, vc_SIP:CLIENT);
connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);
@@ -196,572 +86,16 @@ runs on test_CT return ConnHdlr {
return vc_conn;
}
-private altstep as_Tguard() runs on ConnHdlr {
- [] g_Tguard.timeout {
- setverdict(fail, "Tguard timeout");
- mtc.stop;
- }
-}
-
-private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars)
-runs on ConnHdlr {
- 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 altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on ConnHdlr
-{
- 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 ConnHdlr
-{
- 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 ConnHdlr
-{
- 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 ConnHdlr
-{
- 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 ConnHdlr
-{
- [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
- log("Ignoring ", g_rx_sip_resp);
- repeat;
- }
-}
-
-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(mp_remote_sip_port) },
- { id := "received", paramValue := mp_remote_sip_host }
- }; */
- return tr_Via_from(via_req.viaBody[0].sentBy,
- 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 function f_gen_sdp() runs on ConnHdlr 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 96 97 98 0 8 18 99 100 101\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;
-}
-
-private function f_SIP_register() runs on ConnHdlr return PDU_SIP_Response
-{
- var template (present) PDU_SIP_Response exp;
- var Authorization authorization;
- var Via via := g_pars.local_via;
- var SipAddr from_sipaddr := g_pars.registrar_sip_record;
- 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_sipaddr.params := f_sip_param_set(from_sipaddr.params, "tag", f_sip_rand_tag());
- SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
- g_pars.registrar_sip_call_id,
- from_sipaddr,
- g_pars.registrar_sip_record,
- 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_sipaddr,
- f_tr_To_response(g_pars.registrar_sip_record),
- 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 */
- authorization := f_sip_digest_gen_Authorization(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_sipaddr,
- g_pars.registrar_sip_record,
- via,
- g_pars.registrar_sip_seq_nr,
- g_pars.local_contact,
- ts_Expires("7200"),
- authorization := authorization));
-
- /* Wait for OK answer */
- exp := tr_SIP_Response(
- g_pars.registrar_sip_call_id,
- from_sipaddr,
- f_tr_To_response(g_pars.registrar_sip_record),
- 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;
-}
-
-private function f_SIP_mo_call_setup() runs on ConnHdlr
-{
- var template (value) PDU_SIP_Request req;
- var template (present) PDU_SIP_Response 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 := g_pars.cp.calling;
- g_pars.cp.from_addr.params := f_sip_param_set(g_pars.cp.from_addr.params, "tag", f_sip_rand_tag());
- g_pars.cp.to_addr := g_pars.cp.called;
- branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.cp.from_addr),
- f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
- 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,
- f_tr_From(g_pars.cp.from_addr),
- f_tr_To_response(g_pars.cp.to_addr),
- 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(
- 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,
- g_pars.cp.from_addr,
- f_tr_To_response(g_pars.cp.to_addr),
- 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,
- g_pars.cp.from_addr,
- f_tr_To_response(g_pars.cp.to_addr),
- 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,
- g_pars.cp.from_addr,
- f_tr_To_response(g_pars.cp.to_addr),
- 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 := valueof(ts_SipAddr_from_Addr_Union(g_rx_sip_resp.msgHeader.toField.addressField,
- g_rx_sip_resp.msgHeader.toField.toParams));
-
- /* 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 ConnHdlr
-{
- 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 := valueof(ts_SipAddr_from_Addr_Union(rx_sip_req.msgHeader.fromField.addressField,
- rx_sip_req.msgHeader.fromField.fromParams));
- g_pars.cp.to_addr := valueof(ts_SipAddr_from_Addr_Union(rx_sip_req.msgHeader.toField.addressField,
- rx_sip_req.msgHeader.toField.toParams));
- g_pars.cp.to_addr.params := f_sip_param_set(g_pars.cp.to_addr.params, "tag", f_sip_rand_tag());
- g_pars.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
-}
-
-/* Peer is calling us, accept it: */
-private altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true,
- boolean fail_others := true) runs on ConnHdlr
-{
- var template (present) PDU_SIP_Request exp_req :=
- tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
- ?,
- f_tr_From(g_pars.cp.calling),
- g_pars.cp.called,
- tr_Via_from(f_tr_HostPort(mp_remote_sip_host, mp_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: */
-private altstep as_SIP_mt_call_cancelled(boolean fail_others := true) runs on ConnHdlr
-{
- var template (present) PDU_SIP_Request exp_req :=
- tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
- ?,
- f_tr_From(g_pars.cp.calling),
- g_pars.cp.called,
- tr_Via_from(f_tr_HostPort(mp_remote_sip_host, mp_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) SipAddr 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 := ts_SipAddr_from_Addr_Union(g_rx_sip_req.msgHeader.toField.addressField,
- g_rx_sip_req.msgHeader.toField.toParams);
- 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: */
-private altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on ConnHdlr
-{
- 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(mp_remote_sip_host, mp_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: */
-private function f_SIP_do_call_hangup() runs on ConnHdlr
-{
- 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_SipAddr_to_str(g_pars.cp.from_addr),
- f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
- 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,
- f_tr_To_response(g_pars.cp.to_addr),
- 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: */
-private altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on ConnHdlr
-{
- 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(mp_remote_sip_host, mp_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);
-}
-
/* Test SIP registration of local clients */
-private function f_TC_internal_registration(charstring id) runs on ConnHdlr {
+private function f_TC_internal_registration(charstring id) runs on SIPConnHdlr {
f_SIP_register();
// f_SIP_deregister();
setverdict(pass);
}
testcase TC_internal_registration() runs on test_CT {
- var ConnHdlrPars pars;
- var ConnHdlr vc_conn;
+ var SIPConnHdlrPars pars;
+ var SIPConnHdlr vc_conn;
f_init();
pars := f_init_ConnHdlrPars();
vc_conn := f_start_handler(refers(f_TC_internal_registration), pars);
@@ -769,7 +103,7 @@ testcase TC_internal_registration() runs on test_CT {
}
/* Successful SIP MO-MT Call between local clients: */
-private function f_TC_internal_call_mo(charstring id) runs on ConnHdlr {
+private function f_TC_internal_call_mo(charstring id) runs on SIPConnHdlr {
f_SIP_register();
COORD.send(COORD_CMD_REGISTERED);
@@ -783,7 +117,7 @@ private function f_TC_internal_call_mo(charstring id) runs on ConnHdlr {
setverdict(pass);
}
-private function f_TC_internal_call_mt(charstring id) runs on ConnHdlr {
+private function f_TC_internal_call_mt(charstring id) runs on SIPConnHdlr {
f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.cp.called.addr)));
@@ -807,8 +141,8 @@ private function f_TC_internal_call_mt(charstring id) runs on ConnHdlr {
setverdict(pass);
}
testcase TC_internal_call_momt() runs on test_CT {
- var ConnHdlrPars pars[2];
- var ConnHdlr vc_conn[2];
+ var SIPConnHdlrPars pars[2];
+ var SIPConnHdlr vc_conn[2];
f_init();
@@ -850,11 +184,11 @@ testcase TC_internal_call_momt() runs on test_CT {
* equipments ring (INVITE). The first one to pick up the call (OK 200) gets the
* call established (ACK), others get a CANCEL event. */
private function TC_internal_call_all_Nregistered(integer num_conns := 2) runs on test_CT {
- var ConnHdlrList vc_conn_list := {};
+ var SIPConnHdlrList vc_conn_list := {};
const integer vc_conn_mo_idx := 0; /* Index of MO leg in vc_conn_list */
const integer vc_conn_mt_idx := 1; /* Index of MT leg in vc_conn_list, peer picking up first the call */
var SipAddr broadcast_sip_record;
- var ConnHdlrPars pars_mo;
+ var SIPConnHdlrPars pars_mo;
f_init();
@@ -862,8 +196,8 @@ private function TC_internal_call_all_Nregistered(integer num_conns := 2) runs o
ts_UserInfo(broadcast_sip_extension)));
for (var integer i := 0; i < num_conns; i := i + 1) {
- var ConnHdlrPars pars;
- var ConnHdlr vc_conn;
+ var SIPConnHdlrPars pars;
+ var SIPConnHdlr vc_conn;
pars := f_init_ConnHdlrPars(idx := i + 1);
if (i == vc_conn_mo_idx) { /* MO */
pars.cp.calling := pars.registrar_sip_record;
diff --git a/asterisk/SIP_ConnectionHandler.ttcn b/asterisk/SIP_ConnectionHandler.ttcn
new file mode 100644
index 00000000..13b5f049
--- /dev/null
+++ b/asterisk/SIP_ConnectionHandler.ttcn
@@ -0,0 +1,707 @@
+/* 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,
+ 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,
+
+ SipAddr from_addr optional,
+ SipAddr to_addr optional,
+
+ CallidString sip_call_id,
+ integer sip_seq_nr,
+ charstring sip_body optional,
+
+ charstring local_rtp_addr,
+ uint16_t local_rtp_port,
+
+ 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,
+ 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)
+ })),
+ 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_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 96 97 98 0 8 18 99 100 101\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 SipAddr from_sipaddr := g_pars.registrar_sip_record;
+ 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_sipaddr.params := f_sip_param_set(from_sipaddr.params, "tag", f_sip_rand_tag());
+ SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
+ g_pars.registrar_sip_call_id,
+ from_sipaddr,
+ g_pars.registrar_sip_record,
+ 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_sipaddr,
+ f_tr_To_response(g_pars.registrar_sip_record),
+ 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 */
+ authorization := f_sip_digest_gen_Authorization(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_sipaddr,
+ g_pars.registrar_sip_record,
+ via,
+ g_pars.registrar_sip_seq_nr,
+ g_pars.local_contact,
+ ts_Expires("7200"),
+ authorization := authorization));
+
+ /* Wait for OK answer */
+ exp := tr_SIP_Response(
+ g_pars.registrar_sip_call_id,
+ from_sipaddr,
+ f_tr_To_response(g_pars.registrar_sip_record),
+ 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 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 := g_pars.cp.calling;
+ g_pars.cp.from_addr.params := f_sip_param_set(g_pars.cp.from_addr.params, "tag", f_sip_rand_tag());
+ g_pars.cp.to_addr := g_pars.cp.called;
+ branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.cp.from_addr),
+ f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
+ 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,
+ f_tr_From(g_pars.cp.from_addr),
+ f_tr_To_response(g_pars.cp.to_addr),
+ 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(
+ 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,
+ g_pars.cp.from_addr,
+ f_tr_To_response(g_pars.cp.to_addr),
+ 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,
+ g_pars.cp.from_addr,
+ f_tr_To_response(g_pars.cp.to_addr),
+ 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,
+ g_pars.cp.from_addr,
+ f_tr_To_response(g_pars.cp.to_addr),
+ 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 := valueof(ts_SipAddr_from_Addr_Union(g_rx_sip_resp.msgHeader.toField.addressField,
+ g_rx_sip_resp.msgHeader.toField.toParams));
+
+ /* 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 := valueof(ts_SipAddr_from_Addr_Union(rx_sip_req.msgHeader.fromField.addressField,
+ rx_sip_req.msgHeader.fromField.fromParams));
+ g_pars.cp.to_addr := valueof(ts_SipAddr_from_Addr_Union(rx_sip_req.msgHeader.toField.addressField,
+ rx_sip_req.msgHeader.toField.toParams));
+ g_pars.cp.to_addr.params := f_sip_param_set(g_pars.cp.to_addr.params, "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),
+ ?,
+ f_tr_From(g_pars.cp.calling),
+ g_pars.cp.called,
+ 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),
+ ?,
+ f_tr_From(g_pars.cp.calling),
+ g_pars.cp.called,
+ 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) SipAddr 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 := ts_SipAddr_from_Addr_Union(g_rx_sip_req.msgHeader.toField.addressField,
+ g_rx_sip_req.msgHeader.toField.toParams);
+ 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_SipAddr_to_str(g_pars.cp.from_addr),
+ f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
+ 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,
+ f_tr_To_response(g_pars.cp.to_addr),
+ 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);
+}
+
+}