diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2024-04-18 13:47:07 +0200 |
---|---|---|
committer | Pau Espin Pedrol <pespin@sysmocom.de> | 2024-04-18 19:54:42 +0200 |
commit | caf028d6437d35786c324c12b10944a0277f99df (patch) | |
tree | f4f96fffd7892f92e90145c4f5e03bb0b432c9ea | |
parent | 54b614ad5d44c7c607e8e952b3596e4928e7c560 (diff) |
asterisk: Split SIPConnHdlr to its own file
Change-Id: I168920887bfd05c0a1785c5cb7ac485edaef0df2
-rw-r--r-- | asterisk/Asterisk_Tests.ttcn | 716 | ||||
-rw-r--r-- | asterisk/SIP_ConnectionHandler.ttcn | 707 |
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); +} + +} |