blob: 892f5a33df11b556097769cf43c12db2553b4aaa [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,
Pau Espin Pedrol2e7c28c2024-06-11 16:58:47 +020064 Authorization authorization optional,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +020065 CallPars cp optional
66}
67type record of SIPConnHdlrPars SIPConnHdlrParsList;
68
69type record CallParsMT {
70 /* Whether to wait for COORD.receive(COORD_CMD_PICKUP) before accepting the call. */
71 boolean wait_coord_cmd_pickup,
72 /* Whether to expect CANCEL instead of ACK as answer to our OK */
73 boolean exp_cancel
74}
75template (value) CallParsMT t_CallParsMT := {
76 wait_coord_cmd_pickup := false,
77 exp_cancel := false
78}
79
80type record CallPars {
81 SipAddr calling optional,
82 SipAddr called optional,
83
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +020084 From from_addr optional,
85 To to_addr optional,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +020086
87 CallidString sip_call_id,
88 integer sip_seq_nr,
89 charstring sip_body optional,
90
91 charstring local_rtp_addr,
92 uint16_t local_rtp_port,
93
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +020094 /* Whether to expect Asterisk to re-INVITE to make RTP flow directly to peer. */
95 boolean exp_update_to_direct_rtp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +020096 SDP_Message peer_sdp optional,
97 CallParsMT mt
98}
99
100template (value) CallPars t_CallPars(charstring local_rtp_addr,
101 uint16_t local_rtp_port := 0,
102 template (omit) SipAddr calling := omit,
103 template (omit) SipAddr called := omit) := {
104 calling := calling,
105 called := called,
106 from_addr := omit,
107 to_addr := omit,
108 sip_call_id := hex2str(f_rnd_hexstring(15)),
109 sip_seq_nr := f_sip_rand_seq_nr(),
110 sip_body := omit,
111 local_rtp_addr := local_rtp_addr,
112 local_rtp_port := local_rtp_port,
Pau Espin Pedrol5acf7c62024-06-06 19:23:08 +0200113 exp_update_to_direct_rtp := true,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200114 peer_sdp := omit,
115 mt := t_CallParsMT
116}
117
118template (value) SIPConnHdlrPars t_Pars(charstring local_sip_host,
119 uint16_t local_sip_port,
120 charstring remote_sip_host,
121 uint16_t remote_sip_port,
122 charstring user,
123 charstring display_name := "Anonymous",
124 charstring password := "secret",
125 template (omit) CallPars cp := omit) := {
126 t_guard := 30.0,
127 remote_sip_host := remote_sip_host,
128 remote_sip_port := remote_sip_port,
129 user := user,
130 display_name := f_sip_str_quote(display_name),
131 password := password,
132 registrar_sip_req_uri := valueof(ts_SipUrlHost(remote_sip_host)),
133 registrar_sip_record := ts_SipAddr(ts_HostPort(remote_sip_host),
134 ts_UserInfo(user),
135 f_sip_str_quote(display_name)),
136 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
137 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
138 local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
139 local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
140 ts_UserInfo(user)),
141 local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
142 ts_UserInfo(user)),
143 local_contact := valueof(ts_Contact({
144 ts_ContactAddress(
145 ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
146 local_sip_host,
147 local_sip_port),
148 ts_UserInfo(user))),
149 omit)
150 })),
Pau Espin Pedrol2e7c28c2024-06-11 16:58:47 +0200151 authorization := omit,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200152 cp := cp
153}
154
155private altstep as_Tguard() runs on SIPConnHdlr {
156 [] g_Tguard.timeout {
157 setverdict(fail, "Tguard timeout");
158 mtc.stop;
159 }
160}
161
162type function void_fn(charstring id) runs on SIPConnHdlr;
163function f_handler_init(void_fn fn, charstring id, SIPConnHdlrPars pars)
164runs on SIPConnHdlr {
165 g_name := id;
166 g_pars := pars;
167 g_Tguard.start(pars.t_guard);
168 activate(as_Tguard());
169
170 // Make sure the UA is deregistered before starting the test:
171 // sends REGISTER with Contact = "*" and Expires = 0
172 //f_SIP_deregister();
173
174 /* call the user-supied test case function */
175 fn.apply(id);
176}
177
178private function f_tr_Via_response(Via via_req) return template (present) Via {
179 template (present) SemicolonParam_List via_resp_params := ?;
180
181 /*via_resp_params := {
182 { id := "rport", paramValue := int2str(g_pars.remote_sip_port) },
183 { id := "received", paramValue := g_pars.remote_sip_host }
184 }; */
185 return tr_Via_from(via_req.viaBody[0].sentBy,
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200186 via_req.viaBody[0].sentProtocol.transport,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200187 via_resp_params);
188}
189
190private function f_tr_To_response(template (value) SipAddr to_req) return template (present) SipAddr {
191 return tr_SipAddr_from_val(to_req);
192}
193
194private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
195 return tr_SipAddr_from_val(from_req);
196}
197
198private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on SIPConnHdlr
199{
200 var PDU_SIP_Request sip_req;
201 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
202 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
203 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
204 }
205}
206
207private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on SIPConnHdlr
208{
209 var PDU_SIP_Response sip_resp;
210 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
211 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
212 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
213 }
214}
215
216altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on SIPConnHdlr
217{
218 var charstring sip_expect_str := log2str(sip_expect);
219 [] SIP.receive(sip_expect) -> value g_rx_sip_req;
220 [fail_others] as_SIP_fail_req(sip_expect_str);
221 [fail_others] as_SIP_fail_resp(sip_expect_str);
222}
223
224altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on SIPConnHdlr
225{
226 var charstring sip_expect_str := log2str(sip_expect);
227 [] SIP.receive(sip_expect) -> value g_rx_sip_resp;
228 [fail_others] as_SIP_fail_resp(sip_expect_str);
229 [fail_others] as_SIP_fail_req(sip_expect_str);
230}
231
232altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on SIPConnHdlr
233{
234 [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
235 log("Ignoring ", g_rx_sip_resp);
236 repeat;
237 }
238}
239
240private function f_gen_sdp() runs on SIPConnHdlr return charstring {
241 var charstring sdp :=
242 "v=0\r\n" &
243 "o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
244 "s=Talk\r\n" &
245 "c=IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
246 "t=0 0\r\n" &
247 "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
248 "a=record:off\r\n" &
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200249 "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" &
250 "a=rtpmap:8 PCMA/8000\r\n" &
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200251 "a=rtpmap:96 opus/48000/2\r\n" &
252 "a=fmtp:96 useinbandfec=1\r\n" &
253 "a=rtpmap:97 speex/16000\r\n" &
254 "a=fmtp:97 vbr=on\r\n" &
255 "a=rtpmap:98 speex/8000\r\n" &
256 "a=fmtp:98 vbr=on\r\n" &
257 "a=fmtp:18 annexb=yes\r\n" &
258 "a=rtpmap:99 telephone-event/48000\r\n" &
259 "a=rtpmap:100 telephone-event/16000\r\n" &
260 "a=rtpmap:101 telephone-event/8000\r\n" &
261 "a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" &
262 "a=rtcp-fb:* trr-int 1000\r\n" &
263 "a=rtcp-fb:* ccm tmmbr\r\n";
264 return sdp;
265}
266
267function f_SIP_register() runs on SIPConnHdlr return PDU_SIP_Response
268{
269 var template (present) PDU_SIP_Response exp;
270 var Authorization authorization;
271 var Via via := g_pars.local_via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200272 var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
273 var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
274 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 +0200275 var charstring branch_value;
276
277 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
278 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
279 g_pars.registrar_sip_call_id,
280 g_pars.registrar_sip_seq_nr);
281
282 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200283 from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200284 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
285 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200286 from_addr,
287 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200288 via,
289 g_pars.registrar_sip_seq_nr,
290 g_pars.local_contact,
291 ts_Expires("7200")));
292
293 exp := tr_SIP_Response_Unauthorized(
294 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200295 from_addr,
296 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200297 f_tr_Via_response(via),
298 *,
299 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
300 g_pars.registrar_sip_seq_nr);
301 as_SIP_expect_resp(exp);
302
303 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol2e7c28c2024-06-11 16:58:47 +0200304 g_pars.authorization :=
305 f_sip_digest_gen_Authorization_MD5(g_rx_sip_resp.msgHeader.wwwAuthenticate,
306 g_pars.user, g_pars.password,
307 "REGISTER",
308 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200309
310 /* New transaction: */
311 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
312 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
313 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
314 g_pars.registrar_sip_call_id,
315 g_pars.registrar_sip_seq_nr);
316 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
317
318 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
319 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200320 from_addr,
321 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200322 via,
323 g_pars.registrar_sip_seq_nr,
324 g_pars.local_contact,
325 ts_Expires("7200"),
Pau Espin Pedrol2e7c28c2024-06-11 16:58:47 +0200326 authorization := g_pars.authorization));
327
328 /* Wait for OK answer */
329 exp := tr_SIP_Response(
330 g_pars.registrar_sip_call_id,
331 from_addr,
332 to_addr_exp,
333 f_tr_Via_response(via),
334 *,
335 "REGISTER", 200,
336 g_pars.registrar_sip_seq_nr, "OK");
337 as_SIP_expect_resp(exp);
338
339 /* Prepare for next use: */
340 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
341 return g_rx_sip_resp;
342}
343
344function f_SIP_unregister() runs on SIPConnHdlr return PDU_SIP_Response
345{
346 var template (present) PDU_SIP_Response exp;
347 var Via via := g_pars.local_via;
348 var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
349 var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
350 var template (present) To to_addr_exp := tr_To(tr_Addr_Union_from_val(to_addr.addressField), *);
351 var charstring branch_value;
352
353 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
354 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
355 g_pars.registrar_sip_call_id,
356 g_pars.registrar_sip_seq_nr);
357
358 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
359 from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
360 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
361 g_pars.registrar_sip_call_id,
362 from_addr,
363 to_addr,
364 via,
365 g_pars.registrar_sip_seq_nr,
366 g_pars.local_contact,
367 ts_Expires("0"),
368 authorization := g_pars.authorization));
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200369
370 /* Wait for OK answer */
371 exp := tr_SIP_Response(
372 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200373 from_addr,
374 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200375 f_tr_Via_response(via),
376 *,
377 "REGISTER", 200,
378 g_pars.registrar_sip_seq_nr, "OK");
379 as_SIP_expect_resp(exp);
380
381 /* Prepare for next use: */
382 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
383 return g_rx_sip_resp;
384}
385
386function f_SIP_mo_call_setup() runs on SIPConnHdlr
387{
388 var template (value) PDU_SIP_Request req;
389 var template (present) PDU_SIP_Response exp;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200390 var template (present) From from_addr_exp;
391 var template (present) To to_addr_exp;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200392 var Via via;
393 var charstring tx_sdp := f_gen_sdp();
394 var default d_trying, d_ringing;
395 var charstring branch_value;
396
397 /* RFC 3261 8.1.1.3 From */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200398 g_pars.cp.from_addr := valueof(ts_From(g_pars.cp.calling.addr, g_pars.cp.calling.params));
399 g_pars.cp.from_addr.fromParams := f_sip_param_set(g_pars.cp.from_addr.fromParams, "tag", f_sip_rand_tag());
400 g_pars.cp.to_addr := valueof(ts_To(g_pars.cp.called.addr, g_pars.cp.called.params));
401 from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.cp.from_addr.addressField), *);
402 to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *);
403 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
404 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200405 g_pars.cp.sip_call_id,
406 g_pars.cp.sip_seq_nr);
407 via := g_pars.local_via;
408 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
409
410 req := ts_SIP_INVITE(g_pars.cp.sip_call_id,
411 g_pars.cp.from_addr,
412 g_pars.cp.to_addr,
413 via,
414 g_pars.local_contact,
415 g_pars.cp.sip_seq_nr,
416 body := tx_sdp);
417
418 SIP.send(req);
419
420 /* RFC 3261 22.2: */
421 exp := tr_SIP_Response_Unauthorized(
422 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200423 from_addr_exp,
424 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200425 f_tr_Via_response(via),
426 *,
427 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
428 g_pars.cp.sip_seq_nr, "INVITE");
429 as_SIP_expect_resp(exp);
430
431 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200432 req.msgHeader.authorization := f_sip_digest_gen_Authorization_MD5(
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200433 g_rx_sip_resp.msgHeader.wwwAuthenticate,
434 g_pars.user, g_pars.password,
435 "INVITE",
436 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
437 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
438 f_sip_Request_inc_seq_nr(req);
439 SIP.send(req);
440
441 /* Conditionally match and accept 100 Trying. */
442 exp := tr_SIP_Response_Trying(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200443 from_addr_exp,
444 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200445 f_tr_Via_response(via),
446 g_pars.cp.sip_seq_nr, "INVITE");
447 d_trying := activate(as_SIP_ignore_resp(exp));
448
449 /* Conditionally match and accept 180 Ringing */
450 exp := tr_SIP_Response_Ringing(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200451 from_addr_exp,
452 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200453 f_tr_Via_response(via),
454 g_pars.cp.sip_seq_nr, "INVITE");
455 d_ringing := activate(as_SIP_ignore_resp(exp));
456
457 /* Wait for OK answer */
458 exp := tr_SIP_Response(
459 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200460 from_addr_exp,
461 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200462 f_tr_Via_response(via),
463 *,
464 "INVITE", 200,
465 g_pars.cp.sip_seq_nr, "OK",
466 body := ?);
467 as_SIP_expect_resp(exp, fail_others := false);
468
469 deactivate(d_trying);
470 deactivate(d_ringing);
471
472 /* Update To with the tags received from peer: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200473 g_pars.cp.to_addr := g_rx_sip_resp.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200474
475 /* Transmit ACK */
476 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
477 req := ts_SIP_ACK(g_pars.cp.sip_call_id,
478 g_pars.cp.from_addr,
479 g_pars.cp.to_addr,
480 via,
481 g_pars.cp.sip_seq_nr,
482 omit);
483 SIP.send(req);
484 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
485}
486
487private function f_ConnHdlr_parse_initial_SIP_INVITE(PDU_SIP_Request rx_sip_req) runs on SIPConnHdlr
488{
489 f_SDP_decodeMessage(rx_sip_req.messageBody, g_pars.cp.peer_sdp);
490 log("Rx Initial MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
491
492 /* Obtain params: */
493 g_pars.cp.sip_call_id := rx_sip_req.msgHeader.callId.callid;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200494 g_pars.cp.from_addr := rx_sip_req.msgHeader.fromField;
495 g_pars.cp.to_addr := rx_sip_req.msgHeader.toField;
496 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 +0200497 g_pars.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
498}
499
500/* Peer is calling us, accept it: */
501altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true,
502 boolean fail_others := true) runs on SIPConnHdlr
503{
504 var template (present) PDU_SIP_Request exp_req :=
505 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
506 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200507 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
508 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200509 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
510 ?, ?);
511 var charstring sip_expect_str := log2str(exp_req);
512
513 [] SIP.receive(exp_req) -> value g_rx_sip_req {
514 var template (value) PDU_SIP_Response tx_resp;
515 var Via via;
516 var charstring tx_sdp;
517
518 /* Obtain params: */
519 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
520 via := g_rx_sip_req.msgHeader.via;
521
522
523 /* Tx 180 Ringing */
524 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
525 g_pars.cp.from_addr,
526 g_pars.cp.to_addr,
527 via,
528 g_pars.cp.sip_seq_nr);
529 SIP.send(tx_resp);
530
531 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
532 COORD.receive(COORD_CMD_PICKUP);
533 }
534
535 /* Tx 200 OK */
536 tx_sdp := f_gen_sdp();
537 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
538 g_pars.cp.from_addr,
539 g_pars.cp.to_addr,
540 "INVITE", 200,
541 g_pars.cp.sip_seq_nr,
542 "OK",
543 via,
544 body := tx_sdp);
545 SIP.send(tx_resp);
546
547 /* Wait for ACK */
548 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
549 g_pars.cp.sip_call_id,
550 g_pars.cp.from_addr,
551 g_pars.cp.to_addr,
552 f_tr_Via_response(via),
553 g_pars.cp.sip_seq_nr, *);
554 as_SIP_expect_req(exp_req);
555
556 if (exp_update_to_direct_rtp) {
557 /* Asterisk will now update the session to connect us to MO directly: */
558 /* Via is not kept since anyway "branch" will change upon following INVITE. */
559 as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
560 }
561 }
562 [fail_others] as_SIP_fail_resp(sip_expect_str);
563 [fail_others] as_SIP_fail_req(sip_expect_str);
564
565}
566
567/* Peer is calling us, but cancells it during ringing: */
568altstep as_SIP_mt_call_cancelled(boolean fail_others := true) runs on SIPConnHdlr
569{
570 var template (present) PDU_SIP_Request exp_req :=
571 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
572 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200573 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
574 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200575 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
576 ?, ?);
577 var charstring sip_expect_str := log2str(exp_req);
578
579 [] SIP.receive(exp_req) -> value g_rx_sip_req {
580 var template (value) PDU_SIP_Response tx_resp;
581 var Via via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200582 var template (present) To exp_to_addr;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200583 var charstring tx_sdp;
584
585 /* Obtain params: */
586 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
587 via := g_rx_sip_req.msgHeader.via;
588
589
590 /* Tx 180 Ringing */
591 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
592 g_pars.cp.from_addr,
593 g_pars.cp.to_addr,
594 via,
595 g_pars.cp.sip_seq_nr);
596 SIP.send(tx_resp);
597
598 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
599 COORD.receive(COORD_CMD_PICKUP);
600 }
601
602 /* Wait for CANCEL */
603 /* Cancel may come even before we send Ringing, hence To's "tag"
604 * may not be known by peer, so g_pars.to_addr can't be used here: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200605 exp_to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200606 exp_req := tr_SIP_CANCEL(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
607 g_pars.cp.sip_call_id,
608 g_pars.cp.from_addr,
609 exp_to_addr,
610 f_tr_Via_response(via),
611 g_pars.cp.sip_seq_nr, *);
612 as_SIP_expect_req(exp_req);
613
614 /* Tx 200 OK */
615 tx_sdp := f_gen_sdp();
616 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
617 g_pars.cp.from_addr,
618 g_pars.cp.to_addr,
619 "CANCEL", 200,
620 g_pars.cp.sip_seq_nr,
621 "OK",
622 via,
623 body := omit);
624 SIP.send(tx_resp);
625 }
626 [fail_others] as_SIP_fail_resp(sip_expect_str);
627 [fail_others] as_SIP_fail_req(sip_expect_str);
628
629}
630
631/* New INVITE arrives after MT call is established. Accept it: */
632altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
633{
634 var template (present) PDU_SIP_Request exp_req :=
635 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
636 g_pars.cp.sip_call_id,
637 g_pars.cp.from_addr,
638 g_pars.cp.to_addr,
639 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
640 exp_seq_nr,
641 ?);
642 var charstring sip_expect_str := log2str(exp_req);
643
644 [] SIP.receive(exp_req) -> value g_rx_sip_req {
645 var template (value) PDU_SIP_Response tx_resp;
646 var charstring tx_sdp;
647 var Via via;
648
649 f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
650 log("Rx Update MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
651
652 /* Update parameters: */
653 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
654 /* "branch" has changed: */
655 via := g_rx_sip_req.msgHeader.via;
656
657 /* Tx 200 OK */
658 tx_sdp := f_gen_sdp();
659 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
660 g_pars.cp.from_addr,
661 g_pars.cp.to_addr,
662 "INVITE", 200,
663 g_pars.cp.sip_seq_nr,
664 "OK",
665 via,
666 body := tx_sdp);
667 SIP.send(tx_resp);
668
669 /* Wait for ACK */
670 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
671 g_pars.cp.sip_call_id,
672 g_pars.cp.from_addr,
673 g_pars.cp.to_addr,
674 f_tr_Via_response(via),
675 g_pars.cp.sip_seq_nr, *);
676 as_SIP_expect_req(exp_req);
677 }
678 [fail_others] as_SIP_fail_resp(sip_expect_str);
679 [fail_others] as_SIP_fail_req(sip_expect_str);
680}
681
682/* Tx BYE: */
683function f_SIP_do_call_hangup() runs on SIPConnHdlr
684{
685 var template (value) PDU_SIP_Request req;
686 var template (present) PDU_SIP_Response exp_resp;
687 var Via via;
688 var charstring branch_value;
689
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200690 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
691 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200692 g_pars.cp.sip_call_id,
693 g_pars.cp.sip_seq_nr);
694
695 via := g_pars.local_via;
696 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
697
698 /* Transmit ACK */
699 req := ts_SIP_BYE(g_pars.cp.sip_call_id,
700 g_pars.cp.from_addr,
701 g_pars.cp.to_addr,
702 via,
703 g_pars.cp.sip_seq_nr,
704 omit);
705 SIP.send(req);
706
707 /* Wait for OK answer */
708 exp_resp := tr_SIP_Response(
709 g_pars.cp.sip_call_id,
710 g_pars.cp.from_addr,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200711 tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200712 f_tr_Via_response(via),
713 *,
714 "BYE", 200,
715 g_pars.cp.sip_seq_nr, "OK");
716 as_SIP_expect_resp(exp_resp);
717
718 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
719}
720
721/* Call is terminated by peer: */
722altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
723{
724 var template (present) PDU_SIP_Request exp_req :=
725 tr_SIP_BYE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
726 g_pars.cp.sip_call_id,
727 g_pars.cp.from_addr,
728 g_pars.cp.to_addr,
729 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
730 exp_seq_nr);
731 var charstring sip_expect_str := log2str(exp_req);
732
733 [] SIP.receive(exp_req) -> value g_rx_sip_req {
734 var template (value) PDU_SIP_Response tx_resp;
735 var charstring tx_sdp;
736 var Via via;
737
738 /* Update parameters: */
739 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
740 /* "branch" has changed: */
741 via := g_rx_sip_req.msgHeader.via;
742
743 /* Tx 200 OK */
744 tx_sdp := f_gen_sdp();
745 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
746 g_pars.cp.from_addr,
747 g_pars.cp.to_addr,
748 "BYE", 200,
749 g_pars.cp.sip_seq_nr,
750 "OK",
751 via,
752 body := tx_sdp);
753 SIP.send(tx_resp);
754 }
755 [fail_others] as_SIP_fail_resp(sip_expect_str);
756 [fail_others] as_SIP_fail_req(sip_expect_str);
757}
758
759}