blob: 6ca05c1312bb75679a411609c37d37e591cca4d9 [file] [log] [blame]
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +01001module Asterisk_Tests {
2
3/* Asterisk test suite in TTCN-3
4 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 * All rights reserved.
6 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
7 *
8 * Released under the terms of GNU General Public License, Version 2 or
9 * (at your option) any later version.
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020014import from TCCOpenSecurity_Functions all;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010015import from General_Types all;
16import from Osmocom_Types all;
17import from Native_Functions all;
18import from Misc_Helpers all;
19
20import from SDP_Types all;
21import from SDP_Templates all;
22
23import from SIP_Emulation all;
24import from SIPmsg_Types all;
25import from SIP_Templates all;
26
27modulepar {
28 charstring mp_local_sip_host := "127.0.0.2";
29 integer mp_local_sip_port := 5060;
30 charstring mp_remote_sip_host := "127.0.0.1";
31 integer mp_remote_sip_port := 5060;
32}
33
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020034type port Coord_PT message
35{
36 inout charstring;
37} with { extension "internal" };
38private const charstring COORD_CMD_REGISTERED := "COORD_CMD_REGISTERED";
39private const charstring COORD_CMD_START := "COORD_CMD_START";
40private const charstring COORD_CMD_CALL_ESTABLISHED := "COORD_CMD_CALL_ESTABLISHED";
41private const charstring COORD_CMD_HANGUP := "COORD_CMD_HANGUP";
42
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010043type component test_CT {
44 var SIP_Emulation_CT vc_SIP;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020045 port Coord_PT COORD;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010046}
47
48type component ConnHdlr extends SIP_ConnHdlr {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020049 var charstring g_name;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010050 var ConnHdlrPars g_pars;
51 timer g_Tguard;
52 var PDU_SIP_Request g_rx_sip_req;
53 var PDU_SIP_Response g_rx_sip_resp;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020054
55 port Coord_PT COORD;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010056}
57
58type record ConnHdlrPars {
59 float t_guard,
60 charstring user,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020061 charstring display_name,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020062 charstring password,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020063 SipUrl registrar_sip_req_uri,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010064 SipAddr registrar_sip_record,
65 CallidString registrar_sip_call_id,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010066 integer registrar_sip_seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020067 Via local_via,
68 SipUrl local_sip_url_ext,
69 SipAddr local_sip_record,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010070 Contact local_contact,
71 CallPars cp optional
72}
73
74template (value) ConnHdlrPars t_Pars(charstring user,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020075 charstring display_name := "Anonymous",
76 charstring password := "secret",
77 template (value) CallPars cp := t_CallPars()) := {
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010078 t_guard := 30.0,
79 user := user,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020080 display_name := f_sip_str_quote(display_name),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020081 password := password,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020082 registrar_sip_req_uri := valueof(ts_SipUrlHost(mp_remote_sip_host)),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010083 registrar_sip_record := ts_SipAddr(ts_HostPort(mp_remote_sip_host),
84 ts_UserInfo(user),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020085 f_sip_str_quote(display_name)),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010086 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & mp_local_sip_host,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010087 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020088 local_via := ts_Via_from(ts_HostPort(mp_local_sip_host, mp_local_sip_port)),
89 local_sip_url_ext := ts_SipUrl(ts_HostPort(mp_local_sip_host, mp_local_sip_port),
90 ts_UserInfo(user)),
91 local_sip_record := ts_SipAddr(ts_HostPort(mp_local_sip_host),
92 ts_UserInfo(user)),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +010093 local_contact := valueof(ts_Contact({
94 ts_ContactAddress(
95 ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
96 mp_local_sip_host,
97 mp_local_sip_port),
98 ts_UserInfo(user))),
99 omit)
100 })),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200101 cp := cp
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100102}
103
104function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return ConnHdlrPars {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200105 var template (value) CallPars cp := t_CallPars(idx := idx);
106 var template (value) ConnHdlrPars pars := t_Pars("0" & int2str(500 + idx),
107 cp := cp);
108 return valueof(pars);
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100109}
110
111type record CallPars {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200112 SipAddr calling optional,
113 SipAddr called optional,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100114
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200115 SipAddr from_addr optional,
116 SipAddr to_addr optional,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100117
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100118 CallidString sip_call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200119 integer sip_seq_nr,
120 charstring sip_body optional,
121
122 charstring local_rtp_addr,
123 uint16_t local_rtp_port,
124
125 SDP_Message peer_sdp optional
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100126}
127
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200128private template (value) CallPars t_CallPars(integer idx := 1,
129 template (omit) SipAddr calling := omit,
130 template (omit) SipAddr called := omit) := {
131 calling := calling,
132 called := called,
133 from_addr := omit,
134 to_addr := omit,
135 sip_call_id := hex2str(f_rnd_hexstring(15)),
136 sip_seq_nr := f_sip_rand_seq_nr(),
137 sip_body := omit,
138 local_rtp_addr := mp_local_sip_host,
139 local_rtp_port := 1234 + 2*idx,
140 peer_sdp := omit
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100141}
142
143function f_init() runs on test_CT {
144 f_init_sip(vc_SIP, "Asterisk_Test");
145 log("end of f_init");
146}
147
148type function void_fn(charstring id) runs on ConnHdlr;
149
150function f_start_handler(void_fn fn, ConnHdlrPars pars)
151runs on test_CT return ConnHdlr {
152 var ConnHdlr vc_conn;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200153 var charstring id := testcasename() & "-ConnHdlr-" & pars.user;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100154
Pau Espin Pedrole94a6482024-04-10 13:37:55 +0200155 vc_conn := ConnHdlr.create(id) alive;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100156
157 connect(vc_conn:SIP, vc_SIP:CLIENT);
158 connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);
159
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200160 connect(vc_conn:COORD, self:COORD);
161
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100162 vc_conn.start(f_handler_init(fn, id, pars));
163 return vc_conn;
164}
165
166private altstep as_Tguard() runs on ConnHdlr {
167 [] g_Tguard.timeout {
168 setverdict(fail, "Tguard timeout");
169 mtc.stop;
170 }
171}
172
173private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars)
174runs on ConnHdlr {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200175 g_name := id;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100176 g_pars := pars;
177 g_Tguard.start(pars.t_guard);
178 activate(as_Tguard());
179
180 // Make sure the UA is deregistered before starting the test:
181 // sends REGISTER with Contact = "*" and Expires = 0
182 //f_SIP_deregister();
183
184 /* call the user-supied test case function */
185 fn.apply(id);
186}
187
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200188private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on ConnHdlr
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100189{
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200190 var PDU_SIP_Request sip_req;
191 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
192 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
193 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100194 }
195}
196
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200197private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on ConnHdlr
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100198{
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200199 var PDU_SIP_Response sip_resp;
200 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
201 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
202 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
203 }
204}
205
206altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on ConnHdlr
207{
208 var charstring sip_expect_str := log2str(sip_expect);
209 [] SIP.receive(sip_expect) -> value g_rx_sip_req;
210 [fail_others] as_SIP_fail_req(sip_expect_str);
211 [fail_others] as_SIP_fail_resp(sip_expect_str);
212}
213
214altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on ConnHdlr
215{
216 var charstring sip_expect_str := log2str(sip_expect);
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100217 [] SIP.receive(sip_expect) -> value g_rx_sip_resp;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200218 [fail_others] as_SIP_fail_resp(sip_expect_str);
219 [fail_others] as_SIP_fail_req(sip_expect_str);
220}
221
222altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on ConnHdlr
223{
224 [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
225 log("Ignoring ", g_rx_sip_resp);
226 repeat;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100227 }
228}
229
230private function f_tr_Via_response(Via via_req) return template (present) Via {
231 template (present) SemicolonParam_List via_resp_params := ?;
232
233 /*via_resp_params := {
234 { id := "rport", paramValue := int2str(mp_remote_sip_port) },
235 { id := "received", paramValue := mp_remote_sip_host }
236 }; */
237 return tr_Via_from(via_req.viaBody[0].sentBy,
238 via_resp_params);
239}
240
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200241private function f_tr_To_response(template (value) SipAddr to_req) return template (present) SipAddr {
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200242 return tr_SipAddr_from_val(to_req);
243}
244
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200245private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
246 return tr_SipAddr_from_val(from_req);
247}
248
249private function f_gen_sdp() runs on ConnHdlr return charstring {
250 var charstring sdp :=
251 "v=0\r\n" &
252 "o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
253 "s=Talk\r\n" &
254 "c=IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
255 "t=0 0\r\n" &
256 "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
257 "a=record:off\r\n" &
258 "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 96 97 98 0 8 18 99 100 101\r\n" &
259 "a=rtpmap:96 opus/48000/2\r\n" &
260 "a=fmtp:96 useinbandfec=1\r\n" &
261 "a=rtpmap:97 speex/16000\r\n" &
262 "a=fmtp:97 vbr=on\r\n" &
263 "a=rtpmap:98 speex/8000\r\n" &
264 "a=fmtp:98 vbr=on\r\n" &
265 "a=fmtp:18 annexb=yes\r\n" &
266 "a=rtpmap:99 telephone-event/48000\r\n" &
267 "a=rtpmap:100 telephone-event/16000\r\n" &
268 "a=rtpmap:101 telephone-event/8000\r\n" &
269 "a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" &
270 "a=rtcp-fb:* trr-int 1000\r\n" &
271 "a=rtcp-fb:* ccm tmmbr\r\n";
272 return sdp;
273}
274
275private function f_SIP_register() runs on ConnHdlr return PDU_SIP_Response
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100276{
277 var template (present) PDU_SIP_Response exp;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200278 var Authorization authorization;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200279 var Via via := g_pars.local_via;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200280 var SipAddr from_sipaddr := g_pars.registrar_sip_record;
281 var charstring branch_value;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100282
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200283 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
284 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
285 g_pars.registrar_sip_call_id,
286 g_pars.registrar_sip_seq_nr);
287
288 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
289 from_sipaddr.params := f_sip_param_set(from_sipaddr.params, "tag", f_sip_rand_tag());
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200290 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100291 g_pars.registrar_sip_call_id,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200292 from_sipaddr,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100293 g_pars.registrar_sip_record,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200294 via,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100295 g_pars.registrar_sip_seq_nr,
296 g_pars.local_contact,
297 ts_Expires("7200")));
298
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200299 exp := tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100300 g_pars.registrar_sip_call_id,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200301 from_sipaddr,
302 f_tr_To_response(g_pars.registrar_sip_record),
303 f_tr_Via_response(via),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100304 *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200305 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100306 g_pars.registrar_sip_seq_nr);
307 as_SIP_expect_resp(exp);
308
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200309 /* Digest Auth: RFC 2617 */
310 authorization := f_sip_digest_gen_Authorization(g_rx_sip_resp.msgHeader.wwwAuthenticate,
311 g_pars.user, g_pars.password,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200312 "REGISTER",
313 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200314
315 /* New transaction: */
316 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
317 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
318 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
319 g_pars.registrar_sip_call_id,
320 g_pars.registrar_sip_seq_nr);
321 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
322
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200323 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200324 g_pars.registrar_sip_call_id,
325 from_sipaddr,
326 g_pars.registrar_sip_record,
327 via,
328 g_pars.registrar_sip_seq_nr,
329 g_pars.local_contact,
330 ts_Expires("7200"),
331 authorization := authorization));
332
333 /* Wait for OK answer */
334 exp := tr_SIP_Response(
335 g_pars.registrar_sip_call_id,
336 from_sipaddr,
337 f_tr_To_response(g_pars.registrar_sip_record),
338 f_tr_Via_response(via),
339 *,
340 "REGISTER", 200,
341 g_pars.registrar_sip_seq_nr, "OK");
342 as_SIP_expect_resp(exp);
343
344 /* Prepare for next use: */
345 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100346 return g_rx_sip_resp;
347}
348
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200349private function f_SIP_mo_call_setup() runs on ConnHdlr
350{
351 var template (value) PDU_SIP_Request req;
352 var template (present) PDU_SIP_Response exp;
353 var Via via;
354 var charstring tx_sdp := f_gen_sdp();
355 var default d_trying, d_ringing;
356 var charstring branch_value;
357
358 /* RFC 3261 8.1.1.3 From */
359 g_pars.cp.from_addr := g_pars.cp.calling;
360 g_pars.cp.from_addr.params := f_sip_param_set(g_pars.cp.from_addr.params, "tag", f_sip_rand_tag());
361 g_pars.cp.to_addr := g_pars.cp.called;
362 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.cp.from_addr),
363 f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
364 g_pars.cp.sip_call_id,
365 g_pars.cp.sip_seq_nr);
366 via := g_pars.local_via;
367 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
368
369 req := ts_SIP_INVITE(g_pars.cp.sip_call_id,
370 g_pars.cp.from_addr,
371 g_pars.cp.to_addr,
372 via,
373 g_pars.local_contact,
374 g_pars.cp.sip_seq_nr,
375 body := tx_sdp);
376
377 SIP.send(req);
378
379 /* RFC 3261 22.2: */
380 exp := tr_SIP_Response_Unauthorized(
381 g_pars.cp.sip_call_id,
382 f_tr_From(g_pars.cp.from_addr),
383 f_tr_To_response(g_pars.cp.to_addr),
384 f_tr_Via_response(via),
385 *,
386 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
387 g_pars.cp.sip_seq_nr, "INVITE");
388 as_SIP_expect_resp(exp);
389
390 /* Digest Auth: RFC 2617 */
391 req.msgHeader.authorization := f_sip_digest_gen_Authorization(
392 g_rx_sip_resp.msgHeader.wwwAuthenticate,
393 g_pars.user, g_pars.password,
394 "INVITE",
395 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
396 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
397 f_sip_Request_inc_seq_nr(req);
398 SIP.send(req);
399
400 /* Conditionally match and accept 100 Trying. */
401 exp := tr_SIP_Response_Trying(g_pars.cp.sip_call_id,
402 g_pars.cp.from_addr,
403 f_tr_To_response(g_pars.cp.to_addr),
404 f_tr_Via_response(via),
405 g_pars.cp.sip_seq_nr, "INVITE");
406 d_trying := activate(as_SIP_ignore_resp(exp));
407
408 /* Conditionally match and accept 180 Ringing */
409 exp := tr_SIP_Response_Ringing(g_pars.cp.sip_call_id,
410 g_pars.cp.from_addr,
411 f_tr_To_response(g_pars.cp.to_addr),
412 f_tr_Via_response(via),
413 g_pars.cp.sip_seq_nr, "INVITE");
414 d_ringing := activate(as_SIP_ignore_resp(exp));
415
416 /* Wait for OK answer */
417 exp := tr_SIP_Response(
418 g_pars.cp.sip_call_id,
419 g_pars.cp.from_addr,
420 f_tr_To_response(g_pars.cp.to_addr),
421 f_tr_Via_response(via),
422 *,
423 "INVITE", 200,
424 g_pars.cp.sip_seq_nr, "OK",
425 body := ?);
426 as_SIP_expect_resp(exp, fail_others := false);
427
428 deactivate(d_trying);
429 deactivate(d_ringing);
430
431 /* Update To with the tags received from peer: */
432 g_pars.cp.to_addr := valueof(ts_SipAddr_from_Addr_Union(g_rx_sip_resp.msgHeader.toField.addressField,
433 g_rx_sip_resp.msgHeader.toField.toParams));
434
435 /* Transmit ACK */
436 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
437 req := ts_SIP_ACK(g_pars.cp.sip_call_id,
438 g_pars.cp.from_addr,
439 g_pars.cp.to_addr,
440 via,
441 g_pars.cp.sip_seq_nr,
442 omit);
443 SIP.send(req);
444 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
445}
446
447/* Peer is calling us, accept it: */
448private altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true, boolean fail_others := true) runs on ConnHdlr
449{
450 var template (present) PDU_SIP_Request exp_req :=
451 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
452 ?,
453 f_tr_From(g_pars.cp.calling),
454 g_pars.cp.called,
455 tr_Via_from(f_tr_HostPort(mp_remote_sip_host, mp_remote_sip_port)),
456 ?, ?);
457 var charstring sip_expect_str := log2str(exp_req);
458
459 [] SIP.receive(exp_req) -> value g_rx_sip_req {
460 var template (value) PDU_SIP_Response tx_resp;
461 var Via via;
462 var charstring tx_sdp;
463
464 f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
465 log("Rx Initial MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
466
467 /* Obtain params: */
468 g_pars.cp.sip_call_id := g_rx_sip_req.msgHeader.callId.callid;
469 g_pars.cp.from_addr := valueof(ts_SipAddr_from_Addr_Union(g_rx_sip_req.msgHeader.fromField.addressField,
470 g_rx_sip_req.msgHeader.fromField.fromParams));
471 g_pars.cp.to_addr := valueof(ts_SipAddr_from_Addr_Union(g_rx_sip_req.msgHeader.toField.addressField,
472 g_rx_sip_req.msgHeader.toField.toParams));
473 g_pars.cp.to_addr.params := f_sip_param_set(g_pars.cp.to_addr.params, "tag", f_sip_rand_tag());
474 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
475 via := g_rx_sip_req.msgHeader.via;
476
477
478 /* Tx 180 Ringing */
479 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
480 g_pars.cp.from_addr,
481 g_pars.cp.to_addr,
482 via,
483 g_pars.cp.sip_seq_nr);
484 SIP.send(tx_resp);
485
486 /* Tx 200 OK */
487 tx_sdp := f_gen_sdp();
488 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
489 g_pars.cp.from_addr,
490 g_pars.cp.to_addr,
491 "INVITE", 200,
492 g_pars.cp.sip_seq_nr,
493 "OK",
494 via,
495 body := tx_sdp);
496 SIP.send(tx_resp);
497
498 /* Wait for ACK */
499 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
500 g_pars.cp.sip_call_id,
501 g_pars.cp.from_addr,
502 g_pars.cp.to_addr,
503 f_tr_Via_response(via),
504 g_pars.cp.sip_seq_nr, *);
505 as_SIP_expect_req(exp_req);
506
507 if (exp_update_to_direct_rtp) {
508 /* Asterisk will now update the session to connect us to MO directly: */
509 /* Via is not kept since anyway "branch" will change upon following INVITE. */
510 as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
511 }
512 }
513 [fail_others] as_SIP_fail_resp(sip_expect_str);
514 [fail_others] as_SIP_fail_req(sip_expect_str);
515
516}
517
518/* New INVITE arrives after MT call is established. Accept it: */
519private altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on ConnHdlr
520{
521 var template (present) PDU_SIP_Request exp_req :=
522 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
523 g_pars.cp.sip_call_id,
524 g_pars.cp.from_addr,
525 g_pars.cp.to_addr,
526 tr_Via_from(f_tr_HostPort(mp_remote_sip_host, mp_remote_sip_port)),
527 exp_seq_nr,
528 ?);
529 var charstring sip_expect_str := log2str(exp_req);
530
531 [] SIP.receive(exp_req) -> value g_rx_sip_req {
532 var template (value) PDU_SIP_Response tx_resp;
533 var charstring tx_sdp;
534 var Via via;
535
536 f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
537 log("Rx Update MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
538
539 /* Update parameters: */
540 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
541 /* "branch" has changed: */
542 via := g_rx_sip_req.msgHeader.via;
543
544 /* Tx 200 OK */
545 tx_sdp := f_gen_sdp();
546 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
547 g_pars.cp.from_addr,
548 g_pars.cp.to_addr,
549 "INVITE", 200,
550 g_pars.cp.sip_seq_nr,
551 "OK",
552 via,
553 body := tx_sdp);
554 SIP.send(tx_resp);
555
556 /* Wait for ACK */
557 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
558 g_pars.cp.sip_call_id,
559 g_pars.cp.from_addr,
560 g_pars.cp.to_addr,
561 f_tr_Via_response(via),
562 g_pars.cp.sip_seq_nr, *);
563 as_SIP_expect_req(exp_req);
564 }
565 [fail_others] as_SIP_fail_resp(sip_expect_str);
566 [fail_others] as_SIP_fail_req(sip_expect_str);
567}
568
569/* Tx BYE: */
570private function f_SIP_do_call_hangup() runs on ConnHdlr
571{
572 var template (value) PDU_SIP_Request req;
573 var template (present) PDU_SIP_Response exp_resp;
574 var Via via;
575 var charstring branch_value;
576
577 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.cp.from_addr),
578 f_sip_SipAddr_to_str(valueof(g_pars.cp.to_addr)),
579 g_pars.cp.sip_call_id,
580 g_pars.cp.sip_seq_nr);
581
582 via := g_pars.local_via;
583 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
584
585 /* Transmit ACK */
586 req := ts_SIP_BYE(g_pars.cp.sip_call_id,
587 g_pars.cp.from_addr,
588 g_pars.cp.to_addr,
589 via,
590 g_pars.cp.sip_seq_nr,
591 omit);
592 SIP.send(req);
593
594 /* Wait for OK answer */
595 exp_resp := tr_SIP_Response(
596 g_pars.cp.sip_call_id,
597 g_pars.cp.from_addr,
598 f_tr_To_response(g_pars.cp.to_addr),
599 f_tr_Via_response(via),
600 *,
601 "BYE", 200,
602 g_pars.cp.sip_seq_nr, "OK");
603 as_SIP_expect_resp(exp_resp);
604
605 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
606}
607
608/* Call is terminated by peer: */
609private altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on ConnHdlr
610{
611 var template (present) PDU_SIP_Request exp_req :=
612 tr_SIP_BYE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
613 g_pars.cp.sip_call_id,
614 g_pars.cp.from_addr,
615 g_pars.cp.to_addr,
616 tr_Via_from(f_tr_HostPort(mp_remote_sip_host, mp_remote_sip_port)),
617 exp_seq_nr);
618 var charstring sip_expect_str := log2str(exp_req);
619
620 [] SIP.receive(exp_req) -> value g_rx_sip_req {
621 var template (value) PDU_SIP_Response tx_resp;
622 var charstring tx_sdp;
623 var Via via;
624
625 /* Update parameters: */
626 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
627 /* "branch" has changed: */
628 via := g_rx_sip_req.msgHeader.via;
629
630 /* Tx 200 OK */
631 tx_sdp := f_gen_sdp();
632 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
633 g_pars.cp.from_addr,
634 g_pars.cp.to_addr,
635 "BYE", 200,
636 g_pars.cp.sip_seq_nr,
637 "OK",
638 via,
639 body := tx_sdp);
640 SIP.send(tx_resp);
641 }
642 [fail_others] as_SIP_fail_resp(sip_expect_str);
643 [fail_others] as_SIP_fail_req(sip_expect_str);
644}
645
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200646/* Test SIP registration of local clients */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100647private function f_TC_internal_registration(charstring id) runs on ConnHdlr {
648
649 f_SIP_register();
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100650 // f_SIP_deregister();
651 setverdict(pass);
652}
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100653testcase TC_internal_registration() runs on test_CT {
654 var ConnHdlrPars pars;
655 var ConnHdlr vc_conn;
656 f_init();
657 pars := f_init_ConnHdlrPars();
658 vc_conn := f_start_handler(refers(f_TC_internal_registration), pars);
659 vc_conn.done;
660}
661
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200662/* Successful SIP MO-MT Call between local clients: */
663private function f_TC_internal_call_mo(charstring id) runs on ConnHdlr {
664
665 f_SIP_register();
666 COORD.send(COORD_CMD_REGISTERED);
667
668 COORD.receive(COORD_CMD_START);
669 f_SIP_mo_call_setup();
670 COORD.send(COORD_CMD_CALL_ESTABLISHED);
671
672 COORD.receive(COORD_CMD_HANGUP);
673 f_SIP_do_call_hangup();
674
675 setverdict(pass);
676}
677private function f_TC_internal_call_mt(charstring id) runs on ConnHdlr {
678
679 f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.cp.called.addr)));
680
681 f_SIP_register();
682 COORD.send(COORD_CMD_REGISTERED);
683
684 as_SIP_mt_call_accept();
685 COORD.send(COORD_CMD_CALL_ESTABLISHED);
686
687 /* Once MO hangs up, Asterisk updates us to point RTP to it: */
688 as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
689 as_SIP_exp_call_hangup(g_pars.cp.sip_seq_nr + 1);
690
691 setverdict(pass);
692}
693testcase TC_internal_call_momt() runs on test_CT {
694 var ConnHdlrPars pars[2];
695 var ConnHdlr vc_conn[2];
696
697 f_init();
698
699 pars[0] := f_init_ConnHdlrPars(idx := 1);
700 pars[1] := f_init_ConnHdlrPars(idx := 2);
701
702 pars[0].cp.calling := pars[0].registrar_sip_record;
703 pars[0].cp.called := pars[1].registrar_sip_record;
704
705 pars[1].cp.calling := pars[0].registrar_sip_record;
706 pars[1].cp.called := pars[1].local_sip_record;
707
708 vc_conn[0] := f_start_handler(refers(f_TC_internal_call_mo), pars[0]);
709 vc_conn[1] := f_start_handler(refers(f_TC_internal_call_mt), pars[1]);
710
711 interleave {
712 [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[0];
713 [] COORD.receive(COORD_CMD_REGISTERED) from vc_conn[1];
714 }
715
716 COORD.send(COORD_CMD_START) to vc_conn[0];
717
718 interleave {
719 [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[0];
720 [] COORD.receive(COORD_CMD_CALL_ESTABLISHED) from vc_conn[1];
721 }
722
723 COORD.send(COORD_CMD_HANGUP) to vc_conn[0];
724
725
726 vc_conn[0].done;
727 vc_conn[1].done;
728}
729
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200730testcase TC_selftest() runs on test_CT {
731 f_sip_digest_selftest();
732 setverdict(pass);
733}
734
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100735control {
736 execute( TC_internal_registration() );
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200737 execute( TC_internal_call_momt() );
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100738}
739
740}