blob: cd50be63691f05ec4e63150b6211ab191f89d29e [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
93 SDP_Message peer_sdp optional,
94 CallParsMT mt
95}
96
97template (value) CallPars t_CallPars(charstring local_rtp_addr,
98 uint16_t local_rtp_port := 0,
99 template (omit) SipAddr calling := omit,
100 template (omit) SipAddr called := omit) := {
101 calling := calling,
102 called := called,
103 from_addr := omit,
104 to_addr := omit,
105 sip_call_id := hex2str(f_rnd_hexstring(15)),
106 sip_seq_nr := f_sip_rand_seq_nr(),
107 sip_body := omit,
108 local_rtp_addr := local_rtp_addr,
109 local_rtp_port := local_rtp_port,
110 peer_sdp := omit,
111 mt := t_CallParsMT
112}
113
114template (value) SIPConnHdlrPars t_Pars(charstring local_sip_host,
115 uint16_t local_sip_port,
116 charstring remote_sip_host,
117 uint16_t remote_sip_port,
118 charstring user,
119 charstring display_name := "Anonymous",
120 charstring password := "secret",
121 template (omit) CallPars cp := omit) := {
122 t_guard := 30.0,
123 remote_sip_host := remote_sip_host,
124 remote_sip_port := remote_sip_port,
125 user := user,
126 display_name := f_sip_str_quote(display_name),
127 password := password,
128 registrar_sip_req_uri := valueof(ts_SipUrlHost(remote_sip_host)),
129 registrar_sip_record := ts_SipAddr(ts_HostPort(remote_sip_host),
130 ts_UserInfo(user),
131 f_sip_str_quote(display_name)),
132 registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
133 registrar_sip_seq_nr := f_sip_rand_seq_nr(),
134 local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
135 local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
136 ts_UserInfo(user)),
137 local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
138 ts_UserInfo(user)),
139 local_contact := valueof(ts_Contact({
140 ts_ContactAddress(
141 ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
142 local_sip_host,
143 local_sip_port),
144 ts_UserInfo(user))),
145 omit)
146 })),
147 cp := cp
148}
149
150private altstep as_Tguard() runs on SIPConnHdlr {
151 [] g_Tguard.timeout {
152 setverdict(fail, "Tguard timeout");
153 mtc.stop;
154 }
155}
156
157type function void_fn(charstring id) runs on SIPConnHdlr;
158function f_handler_init(void_fn fn, charstring id, SIPConnHdlrPars pars)
159runs on SIPConnHdlr {
160 g_name := id;
161 g_pars := pars;
162 g_Tguard.start(pars.t_guard);
163 activate(as_Tguard());
164
165 // Make sure the UA is deregistered before starting the test:
166 // sends REGISTER with Contact = "*" and Expires = 0
167 //f_SIP_deregister();
168
169 /* call the user-supied test case function */
170 fn.apply(id);
171}
172
173private function f_tr_Via_response(Via via_req) return template (present) Via {
174 template (present) SemicolonParam_List via_resp_params := ?;
175
176 /*via_resp_params := {
177 { id := "rport", paramValue := int2str(g_pars.remote_sip_port) },
178 { id := "received", paramValue := g_pars.remote_sip_host }
179 }; */
180 return tr_Via_from(via_req.viaBody[0].sentBy,
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200181 via_req.viaBody[0].sentProtocol.transport,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200182 via_resp_params);
183}
184
185private function f_tr_To_response(template (value) SipAddr to_req) return template (present) SipAddr {
186 return tr_SipAddr_from_val(to_req);
187}
188
189private function f_tr_From(template (value) SipAddr from_req) return template (present) SipAddr {
190 return tr_SipAddr_from_val(from_req);
191}
192
193private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on SIPConnHdlr
194{
195 var PDU_SIP_Request sip_req;
196 [] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
197 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
198 log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
199 }
200}
201
202private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on SIPConnHdlr
203{
204 var PDU_SIP_Response sip_resp;
205 [] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
206 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
207 log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
208 }
209}
210
211altstep as_SIP_expect_req(template (present) PDU_SIP_Request sip_expect, boolean fail_others := true) runs on SIPConnHdlr
212{
213 var charstring sip_expect_str := log2str(sip_expect);
214 [] SIP.receive(sip_expect) -> value g_rx_sip_req;
215 [fail_others] as_SIP_fail_req(sip_expect_str);
216 [fail_others] as_SIP_fail_resp(sip_expect_str);
217}
218
219altstep as_SIP_expect_resp(template (present) PDU_SIP_Response sip_expect, boolean fail_others := true) runs on SIPConnHdlr
220{
221 var charstring sip_expect_str := log2str(sip_expect);
222 [] SIP.receive(sip_expect) -> value g_rx_sip_resp;
223 [fail_others] as_SIP_fail_resp(sip_expect_str);
224 [fail_others] as_SIP_fail_req(sip_expect_str);
225}
226
227altstep as_SIP_ignore_resp(template PDU_SIP_Response sip_expect := ?) runs on SIPConnHdlr
228{
229 [] SIP.receive(sip_expect) -> value g_rx_sip_resp {
230 log("Ignoring ", g_rx_sip_resp);
231 repeat;
232 }
233}
234
235private function f_gen_sdp() runs on SIPConnHdlr return charstring {
236 var charstring sdp :=
237 "v=0\r\n" &
238 "o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
239 "s=Talk\r\n" &
240 "c=IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" &
241 "t=0 0\r\n" &
242 "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" &
243 "a=record:off\r\n" &
Pau Espin Pedrol901cede2024-05-30 13:03:42 +0200244 "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" &
245 "a=rtpmap:8 PCMA/8000\r\n" &
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200246 "a=rtpmap:96 opus/48000/2\r\n" &
247 "a=fmtp:96 useinbandfec=1\r\n" &
248 "a=rtpmap:97 speex/16000\r\n" &
249 "a=fmtp:97 vbr=on\r\n" &
250 "a=rtpmap:98 speex/8000\r\n" &
251 "a=fmtp:98 vbr=on\r\n" &
252 "a=fmtp:18 annexb=yes\r\n" &
253 "a=rtpmap:99 telephone-event/48000\r\n" &
254 "a=rtpmap:100 telephone-event/16000\r\n" &
255 "a=rtpmap:101 telephone-event/8000\r\n" &
256 "a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" &
257 "a=rtcp-fb:* trr-int 1000\r\n" &
258 "a=rtcp-fb:* ccm tmmbr\r\n";
259 return sdp;
260}
261
262function f_SIP_register() runs on SIPConnHdlr return PDU_SIP_Response
263{
264 var template (present) PDU_SIP_Response exp;
265 var Authorization authorization;
266 var Via via := g_pars.local_via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200267 var From from_addr := valueof(ts_From(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
268 var To to_addr := valueof(ts_To(g_pars.registrar_sip_record.addr, g_pars.registrar_sip_record.params));
269 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 +0200270 var charstring branch_value;
271
272 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
273 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
274 g_pars.registrar_sip_call_id,
275 g_pars.registrar_sip_seq_nr);
276
277 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200278 from_addr.fromParams := f_sip_param_set(from_addr.fromParams, "tag", f_sip_rand_tag());
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200279 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
280 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200281 from_addr,
282 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200283 via,
284 g_pars.registrar_sip_seq_nr,
285 g_pars.local_contact,
286 ts_Expires("7200")));
287
288 exp := tr_SIP_Response_Unauthorized(
289 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200290 from_addr,
291 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200292 f_tr_Via_response(via),
293 *,
294 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
295 g_pars.registrar_sip_seq_nr);
296 as_SIP_expect_resp(exp);
297
298 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200299 authorization := f_sip_digest_gen_Authorization_MD5(g_rx_sip_resp.msgHeader.wwwAuthenticate,
300 g_pars.user, g_pars.password,
301 "REGISTER",
302 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200303
304 /* New transaction: */
305 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
306 branch_value := f_sip_gen_branch(f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
307 f_sip_SipAddr_to_str(g_pars.registrar_sip_record),
308 g_pars.registrar_sip_call_id,
309 g_pars.registrar_sip_seq_nr);
310 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
311
312 SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_req_uri,
313 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200314 from_addr,
315 to_addr,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200316 via,
317 g_pars.registrar_sip_seq_nr,
318 g_pars.local_contact,
319 ts_Expires("7200"),
320 authorization := authorization));
321
322 /* Wait for OK answer */
323 exp := tr_SIP_Response(
324 g_pars.registrar_sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200325 from_addr,
326 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200327 f_tr_Via_response(via),
328 *,
329 "REGISTER", 200,
330 g_pars.registrar_sip_seq_nr, "OK");
331 as_SIP_expect_resp(exp);
332
333 /* Prepare for next use: */
334 g_pars.registrar_sip_seq_nr := g_pars.registrar_sip_seq_nr + 1;
335 return g_rx_sip_resp;
336}
337
338function f_SIP_mo_call_setup() runs on SIPConnHdlr
339{
340 var template (value) PDU_SIP_Request req;
341 var template (present) PDU_SIP_Response exp;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200342 var template (present) From from_addr_exp;
343 var template (present) To to_addr_exp;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200344 var Via via;
345 var charstring tx_sdp := f_gen_sdp();
346 var default d_trying, d_ringing;
347 var charstring branch_value;
348
349 /* RFC 3261 8.1.1.3 From */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200350 g_pars.cp.from_addr := valueof(ts_From(g_pars.cp.calling.addr, g_pars.cp.calling.params));
351 g_pars.cp.from_addr.fromParams := f_sip_param_set(g_pars.cp.from_addr.fromParams, "tag", f_sip_rand_tag());
352 g_pars.cp.to_addr := valueof(ts_To(g_pars.cp.called.addr, g_pars.cp.called.params));
353 from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.cp.from_addr.addressField), *);
354 to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *);
355 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
356 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200357 g_pars.cp.sip_call_id,
358 g_pars.cp.sip_seq_nr);
359 via := g_pars.local_via;
360 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
361
362 req := ts_SIP_INVITE(g_pars.cp.sip_call_id,
363 g_pars.cp.from_addr,
364 g_pars.cp.to_addr,
365 via,
366 g_pars.local_contact,
367 g_pars.cp.sip_seq_nr,
368 body := tx_sdp);
369
370 SIP.send(req);
371
372 /* RFC 3261 22.2: */
373 exp := tr_SIP_Response_Unauthorized(
374 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200375 from_addr_exp,
376 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200377 f_tr_Via_response(via),
378 *,
379 tr_WwwAuthenticate({tr_Challenge_digestCln(?)}),
380 g_pars.cp.sip_seq_nr, "INVITE");
381 as_SIP_expect_resp(exp);
382
383 /* Digest Auth: RFC 2617 */
Pau Espin Pedrol80b981a2024-06-04 18:37:22 +0200384 req.msgHeader.authorization := f_sip_digest_gen_Authorization_MD5(
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200385 g_rx_sip_resp.msgHeader.wwwAuthenticate,
386 g_pars.user, g_pars.password,
387 "INVITE",
388 f_sip_SipUrl_to_str(g_pars.registrar_sip_req_uri))
389 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
390 f_sip_Request_inc_seq_nr(req);
391 SIP.send(req);
392
393 /* Conditionally match and accept 100 Trying. */
394 exp := tr_SIP_Response_Trying(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200395 from_addr_exp,
396 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200397 f_tr_Via_response(via),
398 g_pars.cp.sip_seq_nr, "INVITE");
399 d_trying := activate(as_SIP_ignore_resp(exp));
400
401 /* Conditionally match and accept 180 Ringing */
402 exp := tr_SIP_Response_Ringing(g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200403 from_addr_exp,
404 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200405 f_tr_Via_response(via),
406 g_pars.cp.sip_seq_nr, "INVITE");
407 d_ringing := activate(as_SIP_ignore_resp(exp));
408
409 /* Wait for OK answer */
410 exp := tr_SIP_Response(
411 g_pars.cp.sip_call_id,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200412 from_addr_exp,
413 to_addr_exp,
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200414 f_tr_Via_response(via),
415 *,
416 "INVITE", 200,
417 g_pars.cp.sip_seq_nr, "OK",
418 body := ?);
419 as_SIP_expect_resp(exp, fail_others := false);
420
421 deactivate(d_trying);
422 deactivate(d_ringing);
423
424 /* Update To with the tags received from peer: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200425 g_pars.cp.to_addr := g_rx_sip_resp.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200426
427 /* Transmit ACK */
428 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
429 req := ts_SIP_ACK(g_pars.cp.sip_call_id,
430 g_pars.cp.from_addr,
431 g_pars.cp.to_addr,
432 via,
433 g_pars.cp.sip_seq_nr,
434 omit);
435 SIP.send(req);
436 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
437}
438
439private function f_ConnHdlr_parse_initial_SIP_INVITE(PDU_SIP_Request rx_sip_req) runs on SIPConnHdlr
440{
441 f_SDP_decodeMessage(rx_sip_req.messageBody, g_pars.cp.peer_sdp);
442 log("Rx Initial MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
443
444 /* Obtain params: */
445 g_pars.cp.sip_call_id := rx_sip_req.msgHeader.callId.callid;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200446 g_pars.cp.from_addr := rx_sip_req.msgHeader.fromField;
447 g_pars.cp.to_addr := rx_sip_req.msgHeader.toField;
448 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 +0200449 g_pars.cp.sip_seq_nr := rx_sip_req.msgHeader.cSeq.seqNumber;
450}
451
452/* Peer is calling us, accept it: */
453altstep as_SIP_mt_call_accept(boolean exp_update_to_direct_rtp := true,
454 boolean fail_others := true) runs on SIPConnHdlr
455{
456 var template (present) PDU_SIP_Request exp_req :=
457 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
458 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200459 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
460 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200461 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
462 ?, ?);
463 var charstring sip_expect_str := log2str(exp_req);
464
465 [] SIP.receive(exp_req) -> value g_rx_sip_req {
466 var template (value) PDU_SIP_Response tx_resp;
467 var Via via;
468 var charstring tx_sdp;
469
470 /* Obtain params: */
471 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
472 via := g_rx_sip_req.msgHeader.via;
473
474
475 /* Tx 180 Ringing */
476 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
477 g_pars.cp.from_addr,
478 g_pars.cp.to_addr,
479 via,
480 g_pars.cp.sip_seq_nr);
481 SIP.send(tx_resp);
482
483 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
484 COORD.receive(COORD_CMD_PICKUP);
485 }
486
487 /* Tx 200 OK */
488 tx_sdp := f_gen_sdp();
489 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
490 g_pars.cp.from_addr,
491 g_pars.cp.to_addr,
492 "INVITE", 200,
493 g_pars.cp.sip_seq_nr,
494 "OK",
495 via,
496 body := tx_sdp);
497 SIP.send(tx_resp);
498
499 /* Wait for ACK */
500 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
501 g_pars.cp.sip_call_id,
502 g_pars.cp.from_addr,
503 g_pars.cp.to_addr,
504 f_tr_Via_response(via),
505 g_pars.cp.sip_seq_nr, *);
506 as_SIP_expect_req(exp_req);
507
508 if (exp_update_to_direct_rtp) {
509 /* Asterisk will now update the session to connect us to MO directly: */
510 /* Via is not kept since anyway "branch" will change upon following INVITE. */
511 as_SIP_exp_call_update(g_pars.cp.sip_seq_nr + 1);
512 }
513 }
514 [fail_others] as_SIP_fail_resp(sip_expect_str);
515 [fail_others] as_SIP_fail_req(sip_expect_str);
516
517}
518
519/* Peer is calling us, but cancells it during ringing: */
520altstep as_SIP_mt_call_cancelled(boolean fail_others := true) runs on SIPConnHdlr
521{
522 var template (present) PDU_SIP_Request exp_req :=
523 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
524 ?,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200525 tr_From(tr_Addr_Union_from_val(g_pars.cp.calling.addr), *),
526 tr_To(tr_Addr_Union_from_val(g_pars.cp.called.addr), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200527 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
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 Via via;
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200534 var template (present) To exp_to_addr;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200535 var charstring tx_sdp;
536
537 /* Obtain params: */
538 f_ConnHdlr_parse_initial_SIP_INVITE(g_rx_sip_req);
539 via := g_rx_sip_req.msgHeader.via;
540
541
542 /* Tx 180 Ringing */
543 tx_resp := ts_SIP_Response_Ringing(g_pars.cp.sip_call_id,
544 g_pars.cp.from_addr,
545 g_pars.cp.to_addr,
546 via,
547 g_pars.cp.sip_seq_nr);
548 SIP.send(tx_resp);
549
550 if (g_pars.cp.mt.wait_coord_cmd_pickup) {
551 COORD.receive(COORD_CMD_PICKUP);
552 }
553
554 /* Wait for CANCEL */
555 /* Cancel may come even before we send Ringing, hence To's "tag"
556 * may not be known by peer, so g_pars.to_addr can't be used here: */
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200557 exp_to_addr := g_rx_sip_req.msgHeader.toField;
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200558 exp_req := tr_SIP_CANCEL(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
559 g_pars.cp.sip_call_id,
560 g_pars.cp.from_addr,
561 exp_to_addr,
562 f_tr_Via_response(via),
563 g_pars.cp.sip_seq_nr, *);
564 as_SIP_expect_req(exp_req);
565
566 /* Tx 200 OK */
567 tx_sdp := f_gen_sdp();
568 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
569 g_pars.cp.from_addr,
570 g_pars.cp.to_addr,
571 "CANCEL", 200,
572 g_pars.cp.sip_seq_nr,
573 "OK",
574 via,
575 body := omit);
576 SIP.send(tx_resp);
577 }
578 [fail_others] as_SIP_fail_resp(sip_expect_str);
579 [fail_others] as_SIP_fail_req(sip_expect_str);
580
581}
582
583/* New INVITE arrives after MT call is established. Accept it: */
584altstep as_SIP_exp_call_update(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
585{
586 var template (present) PDU_SIP_Request exp_req :=
587 tr_SIP_INVITE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
588 g_pars.cp.sip_call_id,
589 g_pars.cp.from_addr,
590 g_pars.cp.to_addr,
591 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
592 exp_seq_nr,
593 ?);
594 var charstring sip_expect_str := log2str(exp_req);
595
596 [] SIP.receive(exp_req) -> value g_rx_sip_req {
597 var template (value) PDU_SIP_Response tx_resp;
598 var charstring tx_sdp;
599 var Via via;
600
601 f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.cp.peer_sdp);
602 log("Rx Update MT INVITE decoded SDP: ", g_pars.cp.peer_sdp);
603
604 /* Update parameters: */
605 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
606 /* "branch" has changed: */
607 via := g_rx_sip_req.msgHeader.via;
608
609 /* Tx 200 OK */
610 tx_sdp := f_gen_sdp();
611 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
612 g_pars.cp.from_addr,
613 g_pars.cp.to_addr,
614 "INVITE", 200,
615 g_pars.cp.sip_seq_nr,
616 "OK",
617 via,
618 body := tx_sdp);
619 SIP.send(tx_resp);
620
621 /* Wait for ACK */
622 exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
623 g_pars.cp.sip_call_id,
624 g_pars.cp.from_addr,
625 g_pars.cp.to_addr,
626 f_tr_Via_response(via),
627 g_pars.cp.sip_seq_nr, *);
628 as_SIP_expect_req(exp_req);
629 }
630 [fail_others] as_SIP_fail_resp(sip_expect_str);
631 [fail_others] as_SIP_fail_req(sip_expect_str);
632}
633
634/* Tx BYE: */
635function f_SIP_do_call_hangup() runs on SIPConnHdlr
636{
637 var template (value) PDU_SIP_Request req;
638 var template (present) PDU_SIP_Response exp_resp;
639 var Via via;
640 var charstring branch_value;
641
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200642 branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField),
643 f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200644 g_pars.cp.sip_call_id,
645 g_pars.cp.sip_seq_nr);
646
647 via := g_pars.local_via;
648 via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value);
649
650 /* Transmit ACK */
651 req := ts_SIP_BYE(g_pars.cp.sip_call_id,
652 g_pars.cp.from_addr,
653 g_pars.cp.to_addr,
654 via,
655 g_pars.cp.sip_seq_nr,
656 omit);
657 SIP.send(req);
658
659 /* Wait for OK answer */
660 exp_resp := tr_SIP_Response(
661 g_pars.cp.sip_call_id,
662 g_pars.cp.from_addr,
Pau Espin Pedrol41b0e072024-05-29 18:25:51 +0200663 tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *),
Pau Espin Pedrolcaf028d2024-04-18 13:47:07 +0200664 f_tr_Via_response(via),
665 *,
666 "BYE", 200,
667 g_pars.cp.sip_seq_nr, "OK");
668 as_SIP_expect_resp(exp_resp);
669
670 g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1;
671}
672
673/* Call is terminated by peer: */
674altstep as_SIP_exp_call_hangup(template (present) integer exp_seq_nr := ?, boolean fail_others := true) runs on SIPConnHdlr
675{
676 var template (present) PDU_SIP_Request exp_req :=
677 tr_SIP_BYE(f_tr_SipUrl_opt_defport(g_pars.local_sip_url_ext),
678 g_pars.cp.sip_call_id,
679 g_pars.cp.from_addr,
680 g_pars.cp.to_addr,
681 tr_Via_from(f_tr_HostPort(g_pars.remote_sip_host, g_pars.remote_sip_port)),
682 exp_seq_nr);
683 var charstring sip_expect_str := log2str(exp_req);
684
685 [] SIP.receive(exp_req) -> value g_rx_sip_req {
686 var template (value) PDU_SIP_Response tx_resp;
687 var charstring tx_sdp;
688 var Via via;
689
690 /* Update parameters: */
691 g_pars.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber;
692 /* "branch" has changed: */
693 via := g_rx_sip_req.msgHeader.via;
694
695 /* Tx 200 OK */
696 tx_sdp := f_gen_sdp();
697 tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
698 g_pars.cp.from_addr,
699 g_pars.cp.to_addr,
700 "BYE", 200,
701 g_pars.cp.sip_seq_nr,
702 "OK",
703 via,
704 body := tx_sdp);
705 SIP.send(tx_resp);
706 }
707 [fail_others] as_SIP_fail_resp(sip_expect_str);
708 [fail_others] as_SIP_fail_req(sip_expect_str);
709}
710
711}