blob: 05f872f214045c10ec5520b0b7f64034d583432d [file] [log] [blame]
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +02001/* Component implementing a SIP UA towards Asterisk
2 * (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
3 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
4 * All rights reserved.
5 *
6 * Released under the terms of GNU General Public License, Version 2 or
7 * (at your option) any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11module SIP_ConnectionHandler {
12
13import from TCCOpenSecurity_Functions all;
14import from General_Types all;
15import from Osmocom_Types all;
16import from Native_Functions all;
17import from Misc_Helpers all;
18
19import from SDP_Types all;
20import from SDP_Templates all;
21
22import from SIP_Emulation all;
23import from SIPmsg_Types all;
24import from SIP_Templates all;
25
26type port Coord_PT message
27{
28 inout charstring;
29} with { extension "internal" };
30
31const charstring COORD_CMD_REGISTERED := "COORD_CMD_REGISTERED";
32const charstring COORD_CMD_START := "COORD_CMD_START";
33const charstring COORD_CMD_PICKUP := "COORD_CMD_PICKUP";
34const charstring COORD_CMD_CALL_ESTABLISHED := "COORD_CMD_CALL_ESTABLISHED";
35const charstring COORD_CMD_CALL_CANCELLED := "COORD_CMD_CALL_CANCELLED";
36const charstring COORD_CMD_HANGUP := "COORD_CMD_HANGUP";
37
38type component SIPConnHdlr extends SIP_ConnHdlr {
39 var charstring g_name;
40 var SIPConnHdlrPars g_pars;
41 timer g_Tguard;
42 var PDU_SIP_Request g_rx_sip_req;
43 var PDU_SIP_Response g_rx_sip_resp;
44
45 port Coord_PT COORD;
46}
47type record of SIPConnHdlr SIPConnHdlrList;
48
49type record SIPConnHdlrPars {
50 float t_guard,
51 charstring remote_sip_host,
52 uint16_t remote_sip_port,
53 charstring user,
54 charstring display_name,
55 charstring password,
56 SipUrl registrar_sip_req_uri,
57 SipAddr registrar_sip_record,
58 CallidString registrar_sip_call_id,
59 integer registrar_sip_seq_nr,
60 Via local_via,
61 SipUrl local_sip_url_ext,
62 SipAddr local_sip_record,
63 Contact local_contact,
64 CallPars cp optional
65}
66type record of SIPConnHdlrPars SIPConnHdlrParsList;
67
68type record CallParsMT {
69 /* Whether to wait for COORD.receive(COORD_CMD_PICKUP) before accepting the call. */
70 boolean wait_coord_cmd_pickup,
71 /* Whether to expect CANCEL instead of ACK as answer to our OK */
72 boolean exp_cancel
73}
74template (value) CallParsMT t_CallParsMT := {
75 wait_coord_cmd_pickup := false,
76 exp_cancel := false
77}
78
79type record CallPars {
80 SipAddr calling optional,
81 SipAddr called optional,
82
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +020083 From from_addr optional,
84 To to_addr optional,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +020085
86 CallidString sip_call_id,
87 integer sip_seq_nr,
88 charstring sip_body optional,
89
90 charstring local_rtp_addr,
91 uint16_t local_rtp_port,
92
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +020093 /* Whether to expect Asterisk to re-INVITE to make RTP flow directly to peer. */
94 boolean exp_update_to_direct_rtp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +020095 SDP_Message peer_sdp optional,
96 CallParsMT mt
97}
98
99template (value) CallPars t_CallPars(charstring local_rtp_addr,
100 uint16_t local_rtp_port := 0,
101 template (omit) SipAddr calling := omit,
102 template (omit) SipAddr called := omit) := {
103 calling := calling,
104 called := called,
105 from_addr := omit,
106 to_addr := omit,
107 sip_call_id := hex2str(f_rnd_hexstring(15)),
108 sip_seq_nr := f_sip_rand_seq_nr(),
109 sip_body := omit,
110 local_rtp_addr := local_rtp_addr,
111 local_rtp_port := local_rtp_port,
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200112 exp_update_to_direct_rtp := true,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200113 peer_sdp := omit,
114 mt := t_CallParsMT
115}
116
117template (value) SIPConnHdlrPars t_Pars(charstring local_sip_host,
118 uint16_t local_sip_port,
119 charstring remote_sip_host,
120 uint16_t remote_sip_port,
121 charstring user,
122 charstring display_name := "Anonymous",
123 charstring password := "secret",
124 template (omit) CallPars cp := omit) := {
125 t_guard := 30.0,
126 remote_sip_host := remote_sip_host,
127 remote_sip_port := remote_sip_port,
128 user := user,
129 display_name := f_sip_str_quote(display_name),
130 password := password,
131 registrar_sip_req_uri := valueof(ts_SipUrlHost(remote_sip_host)),
132 registrar_sip_record := ts_SipAddr(ts_HostPort(remote_sip_host),
133 ts_UserInfo(user),
134 f_sip_str_quote(display_name)),
135 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
136 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
137 local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
138 local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
139 ts_UserInfo(user)),
140 local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
141 ts_UserInfo(user)),
142 local_contact := valueof(ts_Contact({
143 ts_ContactAddress(
144 ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
145 local_sip_host,
146 local_sip_port),
147 ts_UserInfo(user))),
148 omit)
149 })),
150 cp := cp
151}
152
153private altstep as_Tguard() runs on SIPConnHdlr {
154 [] g_Tguard.timeout {
155 setverdict(fail, "Tguard timeout");
156 mtc.stop;
157 }
158}
159
160type function void_fn(charstring id) runs on SIPConnHdlr;
161function f_handler_init(void_fn fn, charstring id, SIPConnHdlrPars pars)
162runs on SIPConnHdlr {
163 g_name := id;
164 g_pars := pars;
165 g_Tguard.start(pars.t_guard);
166 activate(as_Tguard());
167
168 // Make sure the UA is deregistered before starting the test:
169 // sends REGISTER with Contact = "*" and Expires = 0
170 //f_SIP_deregister();
171
172 /* call the user-supied test case function */
173 fn.apply(id);
174}
175
176private function f_tr_Via_response(Via via_req) return template (present) Via {
177 template (present) SemicolonParam_List via_resp_params := ?;
178
179 /*via_resp_params := {
180 { id := "rport", paramValue := int2str(g_pars.remote_sip_port) },
181 { id := "received", paramValue := g_pars.remote_sip_host }
182 }; */
183 return tr_Via_from(via_req.viaBody[0].sentBy,
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200184 via_req.viaBody[0].sentProtocol.transport,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200185 via_resp_params);
186}
187
188private function f_tr_To_response(template (value) SipAddr to_req) return template (present) SipAddr {
189 return tr_SipAddr_from_val(to_req);
190}
191
192private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
193 return tr_SipAddr_from_val(from_req);
194}
195
196private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on SIPConnHdlr
197{
198 var PDU_SIP_Request sip_req;
199 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
200 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
201 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
202 }
203}
204
205private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on SIPConnHdlr
206{
207 var PDU_SIP_Response sip_resp;
208 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
209 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
210 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
211 }
212}
213
214altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on SIPConnHdlr
215{
216 var charstring sip_expect_str := log2str(sip_expect);
217 [] SIP.receive(sip_expect) -> value g_rx_sip_req;
218 [fail_others] as_SIP_fail_req(sip_expect_str);
219 [fail_others] as_SIP_fail_resp(sip_expect_str);
220}
221
222altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on SIPConnHdlr
223{
224 var charstring sip_expect_str := log2str(sip_expect);
225 [] SIP.receive(sip_expect) -> value g_rx_sip_resp;
226 [fail_others] as_SIP_fail_resp(sip_expect_str);
227 [fail_others] as_SIP_fail_req(sip_expect_str);
228}
229
230altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on SIPConnHdlr
231{
232 [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
233 log("Ignoring ", g_rx_sip_resp);
234 repeat;
235 }
236}
237
238private function f_gen_sdp() runs on SIPConnHdlr return charstring {
239 var charstring sdp :=
240 "v=0\r\n" &
241 "o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
242 "s=Talk\r\n" &
243 "c=IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
244 "t=0 0\r\n" &
245 "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
246 "a=record:off\r\n" &
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200247 "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" &
248 "a=rtpmap:8 PCMA/8000\r\n" &
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200249 "a=rtpmap:96 opus/48000/2\r\n" &
250 "a=fmtp:96 useinbandfec=1\r\n" &
251 "a=rtpmap:97 speex/16000\r\n" &
252 "a=fmtp:97 vbr=on\r\n" &
253 "a=rtpmap:98 speex/8000\r\n" &
254 "a=fmtp:98 vbr=on\r\n" &
255 "a=fmtp:18 annexb=yes\r\n" &
256 "a=rtpmap:99 telephone-event/48000\r\n" &
257 "a=rtpmap:100 telephone-event/16000\r\n" &
258 "a=rtpmap:101 telephone-event/8000\r\n" &
259 "a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" &
260 "a=rtcp-fb:* trr-int 1000\r\n" &
261 "a=rtcp-fb:* ccm tmmbr\r\n";
262 return sdp;
263}
264
265function f_SIP_register() runs on SIPConnHdlr return PDU_SIP_Response
266{
267 var template (present) PDU_SIP_Response exp;
268 var Authorization authorization;
269 var Via via := g_pars.local_via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200270 var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
271 var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
272 var template (present) To to_addr_exp := tr_To(tr_Addr_Union_from_val(to_addr.addressField), *);
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200273 var charstring branch_value;
274
275 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
276 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
277 g_pars.registrar_sip_call_id,
278 g_pars.registrar_sip_seq_nr);
279
280 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200281 from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200282 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
283 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200284 from_addr,
285 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200286 via,
287 g_pars.registrar_sip_seq_nr,
288 g_pars.local_contact,
289 ts_Expires("7200")));
290
291 exp := tr_SIP_Response_Unauthorized(
292 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200293 from_addr,
294 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200295 f_tr_Via_response(via),
296 *,
297 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
298 g_pars.registrar_sip_seq_nr);
299 as_SIP_expect_resp(exp);
300
301 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200302 authorization := f_sip_digest_gen_Authorization_MD5(g_rx_sip_resp.msgHeader.wwwAuthenticate,
303 g_pars.user, g_pars.password,
304 "REGISTER",
305 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200306
307 /* New transaction: */
308 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
309 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
310 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
311 g_pars.registrar_sip_call_id,
312 g_pars.registrar_sip_seq_nr);
313 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
314
315 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
316 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200317 from_addr,
318 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200319 via,
320 g_pars.registrar_sip_seq_nr,
321 g_pars.local_contact,
322 ts_Expires("7200"),
323 authorization := authorization));
324
325 /* Wait for OK answer */
326 exp := tr_SIP_Response(
327 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200328 from_addr,
329 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200330 f_tr_Via_response(via),
331 *,
332 "REGISTER", 200,
333 g_pars.registrar_sip_seq_nr, "OK");
334 as_SIP_expect_resp(exp);
335
336 /* Prepare for next use: */
337 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
338 return g_rx_sip_resp;
339}
340
341function f_SIP_mo_call_setup() runs on SIPConnHdlr
342{
343 var template (value) PDU_SIP_Request req;
344 var template (present) PDU_SIP_Response exp;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200345 var template (present) From from_addr_exp;
346 var template (present) To to_addr_exp;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200347 var Via via;
348 var charstring tx_sdp := f_gen_sdp();
349 var default d_trying, d_ringing;
350 var charstring branch_value;
351
352 /* RFC 3261 8.1.1.3 From */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200353 g_pars.cp.from_addr := valueof(ts_From(g_pars.cp.calling.addr, g_pars.cp.calling.params));
354 g_pars.cp.from_addr.fromParams := f_sip_param_set(g_pars.cp.from_addr.fromParams, "tag", f_sip_rand_tag());
355 g_pars.cp.to_addr := valueof(ts_To(g_pars.cp.called.addr, g_pars.cp.called.params));
356 from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.cp.from_addr.addressField), *);
357 to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *);
358 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
359 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200360 g_pars.cp.sip_call_id,
361 g_pars.cp.sip_seq_nr);
362 via := g_pars.local_via;
363 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
364
365 req := ts_SIP_INVITE(g_pars.cp.sip_call_id,
366 g_pars.cp.from_addr,
367 g_pars.cp.to_addr,
368 via,
369 g_pars.local_contact,
370 g_pars.cp.sip_seq_nr,
371 body := tx_sdp);
372
373 SIP.send(req);
374
375 /* RFC 3261 22.2: */
376 exp := tr_SIP_Response_Unauthorized(
377 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200378 from_addr_exp,
379 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200380 f_tr_Via_response(via),
381 *,
382 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
383 g_pars.cp.sip_seq_nr, "INVITE");
384 as_SIP_expect_resp(exp);
385
386 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200387 req.msgHeader.authorization := f_sip_digest_gen_Authorization_MD5(
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200388 g_rx_sip_resp.msgHeader.wwwAuthenticate,
389 g_pars.user, g_pars.password,
390 "INVITE",
391 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
392 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
393 f_sip_Request_inc_seq_nr(req);
394 SIP.send(req);
395
396 /* Conditionally match and accept 100 Trying. */
397 exp := tr_SIP_Response_Trying(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200398 from_addr_exp,
399 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200400 f_tr_Via_response(via),
401 g_pars.cp.sip_seq_nr, "INVITE");
402 d_trying := activate(as_SIP_ignore_resp(exp));
403
404 /* Conditionally match and accept 180 Ringing */
405 exp := tr_SIP_Response_Ringing(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200406 from_addr_exp,
407 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200408 f_tr_Via_response(via),
409 g_pars.cp.sip_seq_nr, "INVITE");
410 d_ringing := activate(as_SIP_ignore_resp(exp));
411
412 /* Wait for OK answer */
413 exp := tr_SIP_Response(
414 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200415 from_addr_exp,
416 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200417 f_tr_Via_response(via),
418 *,
419 "INVITE", 200,
420 g_pars.cp.sip_seq_nr, "OK",
421 body := ?);
422 as_SIP_expect_resp(exp, fail_others := false);
423
424 deactivate(d_trying);
425 deactivate(d_ringing);
426
427 /* Update To with the tags received from peer: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200428 g_pars.cp.to_addr := g_rx_sip_resp.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200429
430 /* Transmit ACK */
431 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
432 req := ts_SIP_ACK(g_pars.cp.sip_call_id,
433 g_pars.cp.from_addr,
434 g_pars.cp.to_addr,
435 via,
436 g_pars.cp.sip_seq_nr,
437 omit);
438 SIP.send(req);
439 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
440}
441
442private function f_ConnHdlr_parse_initial_SIP_INVITE(PDU_SIP_Request rx_sip_req) runs on SIPConnHdlr
443{
444 f_SDP_decodeMessage(rx_sip_req.messageBody, g_pars.cp.peer_sdp);
445 log("Rx Initial MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
446
447 /* Obtain params: */
448 g_pars.cp.sip_call_id := rx_sip_req.msgHeader.callId.callid;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200449 g_pars.cp.from_addr := rx_sip_req.msgHeader.fromField;
450 g_pars.cp.to_addr := rx_sip_req.msgHeader.toField;
451 g_pars.cp.to_addr.toParams := f_sip_param_set(g_pars.cp.to_addr.toParams, "tag", f_sip_rand_tag());
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200452 g_pars.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
453}
454
455/* Peer is calling us, accept it: */
456altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true,
457 boolean fail_others := true) runs on SIPConnHdlr
458{
459 var template (present) PDU_SIP_Request exp_req :=
460 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
461 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200462 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
463 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200464 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
465 ?, ?);
466 var charstring sip_expect_str := log2str(exp_req);
467
468 [] SIP.receive(exp_req) -> value g_rx_sip_req {
469 var template (value) PDU_SIP_Response tx_resp;
470 var Via via;
471 var charstring tx_sdp;
472
473 /* Obtain params: */
474 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
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 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
487 COORD.receive(COORD_CMD_PICKUP);
488 }
489
490 /* Tx 200 OK */
491 tx_sdp := f_gen_sdp();
492 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
493 g_pars.cp.from_addr,
494 g_pars.cp.to_addr,
495 "INVITE", 200,
496 g_pars.cp.sip_seq_nr,
497 "OK",
498 via,
499 body := tx_sdp);
500 SIP.send(tx_resp);
501
502 /* Wait for ACK */
503 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
504 g_pars.cp.sip_call_id,
505 g_pars.cp.from_addr,
506 g_pars.cp.to_addr,
507 f_tr_Via_response(via),
508 g_pars.cp.sip_seq_nr, *);
509 as_SIP_expect_req(exp_req);
510
511 if (exp_update_to_direct_rtp) {
512 /* Asterisk will now update the session to connect us to MO directly: */
513 /* Via is not kept since anyway "branch" will change upon following INVITE. */
514 as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
515 }
516 }
517 [fail_others] as_SIP_fail_resp(sip_expect_str);
518 [fail_others] as_SIP_fail_req(sip_expect_str);
519
520}
521
522/* Peer is calling us, but cancells it during ringing: */
523altstep as_SIP_mt_call_cancelled(boolean fail_others := true) runs on SIPConnHdlr
524{
525 var template (present) PDU_SIP_Request exp_req :=
526 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
527 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200528 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
529 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200530 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
531 ?, ?);
532 var charstring sip_expect_str := log2str(exp_req);
533
534 [] SIP.receive(exp_req) -> value g_rx_sip_req {
535 var template (value) PDU_SIP_Response tx_resp;
536 var Via via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200537 var template (present) To exp_to_addr;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200538 var charstring tx_sdp;
539
540 /* Obtain params: */
541 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
542 via := g_rx_sip_req.msgHeader.via;
543
544
545 /* Tx 180 Ringing */
546 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
547 g_pars.cp.from_addr,
548 g_pars.cp.to_addr,
549 via,
550 g_pars.cp.sip_seq_nr);
551 SIP.send(tx_resp);
552
553 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
554 COORD.receive(COORD_CMD_PICKUP);
555 }
556
557 /* Wait for CANCEL */
558 /* Cancel may come even before we send Ringing, hence To's "tag"
559 * may not be known by peer, so g_pars.to_addr can't be used here: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200560 exp_to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200561 exp_req := tr_SIP_CANCEL(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
562 g_pars.cp.sip_call_id,
563 g_pars.cp.from_addr,
564 exp_to_addr,
565 f_tr_Via_response(via),
566 g_pars.cp.sip_seq_nr, *);
567 as_SIP_expect_req(exp_req);
568
569 /* Tx 200 OK */
570 tx_sdp := f_gen_sdp();
571 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
572 g_pars.cp.from_addr,
573 g_pars.cp.to_addr,
574 "CANCEL", 200,
575 g_pars.cp.sip_seq_nr,
576 "OK",
577 via,
578 body := omit);
579 SIP.send(tx_resp);
580 }
581 [fail_others] as_SIP_fail_resp(sip_expect_str);
582 [fail_others] as_SIP_fail_req(sip_expect_str);
583
584}
585
586/* New INVITE arrives after MT call is established. Accept it: */
587altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
588{
589 var template (present) PDU_SIP_Request exp_req :=
590 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
591 g_pars.cp.sip_call_id,
592 g_pars.cp.from_addr,
593 g_pars.cp.to_addr,
594 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
595 exp_seq_nr,
596 ?);
597 var charstring sip_expect_str := log2str(exp_req);
598
599 [] SIP.receive(exp_req) -> value g_rx_sip_req {
600 var template (value) PDU_SIP_Response tx_resp;
601 var charstring tx_sdp;
602 var Via via;
603
604 f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
605 log("Rx Update MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
606
607 /* Update parameters: */
608 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
609 /* "branch" has changed: */
610 via := g_rx_sip_req.msgHeader.via;
611
612 /* Tx 200 OK */
613 tx_sdp := f_gen_sdp();
614 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
615 g_pars.cp.from_addr,
616 g_pars.cp.to_addr,
617 "INVITE", 200,
618 g_pars.cp.sip_seq_nr,
619 "OK",
620 via,
621 body := tx_sdp);
622 SIP.send(tx_resp);
623
624 /* Wait for ACK */
625 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
626 g_pars.cp.sip_call_id,
627 g_pars.cp.from_addr,
628 g_pars.cp.to_addr,
629 f_tr_Via_response(via),
630 g_pars.cp.sip_seq_nr, *);
631 as_SIP_expect_req(exp_req);
632 }
633 [fail_others] as_SIP_fail_resp(sip_expect_str);
634 [fail_others] as_SIP_fail_req(sip_expect_str);
635}
636
637/* Tx BYE: */
638function f_SIP_do_call_hangup() runs on SIPConnHdlr
639{
640 var template (value) PDU_SIP_Request req;
641 var template (present) PDU_SIP_Response exp_resp;
642 var Via via;
643 var charstring branch_value;
644
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200645 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
646 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200647 g_pars.cp.sip_call_id,
648 g_pars.cp.sip_seq_nr);
649
650 via := g_pars.local_via;
651 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
652
653 /* Transmit ACK */
654 req := ts_SIP_BYE(g_pars.cp.sip_call_id,
655 g_pars.cp.from_addr,
656 g_pars.cp.to_addr,
657 via,
658 g_pars.cp.sip_seq_nr,
659 omit);
660 SIP.send(req);
661
662 /* Wait for OK answer */
663 exp_resp := tr_SIP_Response(
664 g_pars.cp.sip_call_id,
665 g_pars.cp.from_addr,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200666 tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200667 f_tr_Via_response(via),
668 *,
669 "BYE", 200,
670 g_pars.cp.sip_seq_nr, "OK");
671 as_SIP_expect_resp(exp_resp);
672
673 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
674}
675
676/* Call is terminated by peer: */
677altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
678{
679 var template (present) PDU_SIP_Request exp_req :=
680 tr_SIP_BYE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
681 g_pars.cp.sip_call_id,
682 g_pars.cp.from_addr,
683 g_pars.cp.to_addr,
684 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
685 exp_seq_nr);
686 var charstring sip_expect_str := log2str(exp_req);
687
688 [] SIP.receive(exp_req) -> value g_rx_sip_req {
689 var template (value) PDU_SIP_Response tx_resp;
690 var charstring tx_sdp;
691 var Via via;
692
693 /* Update parameters: */
694 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
695 /* "branch" has changed: */
696 via := g_rx_sip_req.msgHeader.via;
697
698 /* Tx 200 OK */
699 tx_sdp := f_gen_sdp();
700 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
701 g_pars.cp.from_addr,
702 g_pars.cp.to_addr,
703 "BYE", 200,
704 g_pars.cp.sip_seq_nr,
705 "OK",
706 via,
707 body := tx_sdp);
708 SIP.send(tx_resp);
709 }
710 [fail_others] as_SIP_fail_resp(sip_expect_str);
711 [fail_others] as_SIP_fail_req(sip_expect_str);
712}
713
714}