blob: d35813ee8eea525cc9ae31fc99a3b0182ef05835 [file] [log] [blame]
Harald Welteb0d93602018-03-20 18:09:34 +01001module SIP_Templates {
2
3import from SIPmsg_Types all;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02004import from TCCConversion_Functions all;
5import from TCCOpenSecurity_Functions all;
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02006import from TCCDateTime_Functions all;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02007import from Native_Functions all;
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01008import from Osmocom_Types all;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02009import from Misc_Helpers all;
Harald Welteb0d93602018-03-20 18:09:34 +010010
11/* wrapper type to encapsulate the Addr_Union + parameter list used in From, To. ... */
12type record SipAddr {
13 Addr_Union addr,
14 SemicolonParam_List params optional
15}
16
17const charstring c_SIP_VERSION := "SIP/2.0";
18
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020019template (value) GenericParam ts_Param(template (value) charstring id,
20 template (omit) charstring paramValue := omit) := {
21 id := id,
22 paramValue := paramValue
23}
24template (present) GenericParam tr_Param(template (present) charstring id := ?,
25 template charstring paramValue := *) := {
26 id := id,
27 paramValue := paramValue
28}
29function f_ts_Param_omit(template (value) charstring id,
30 template (omit) charstring paramValue := omit)
31 return template (omit) GenericParam
32{
33 if (istemplatekind(paramValue, "omit")) {
34 return omit;
35 }
36 return ts_Param(id, paramValue);
37}
38
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010039template (value) SipUrl ts_SipUrl(template (value) HostPort host_port,
40 template (omit) UserInfo user_info := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +010041 scheme := "sip",
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010042 userInfo := user_info,
43 hostPort := host_port,
Harald Welteb0d93602018-03-20 18:09:34 +010044 urlParameters := omit,
45 headers := omit
46}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010047template (present) SipUrl tr_SipUrl(template (present) HostPort host_port := ?,
48 template UserInfo user_info := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +010049 scheme := "sip",
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010050 userInfo := user_info,
51 hostPort := host_port,
Harald Welteb0d93602018-03-20 18:09:34 +010052 urlParameters := *,
53 headers := *
54}
55
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020056template (value) SipUrl ts_SipUrlHost(template (value) charstring host,
57 template (omit) integer portField := omit)
58 := ts_SipUrl(ts_HostPort(host, portField));
59
60function ts_SipUrl_from_Addr_Union(template (value) Addr_Union au)
61return template (value) SipUrl {
62 if (ischosen(au.nameAddr)) {
63 return au.nameAddr.addrSpec;
64 } else { /* au.addrSpecUnion */
65 return au.addrSpecUnion;
66 }
67}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010068
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020069template (value) Credentials ts_Credentials_DigestResponse(template (value) CommaParam_List digestResponse) := {
70 digestResponse := digestResponse
71}
72
73template (value) Credentials ts_Credentials_DigestResponseMD5(
74 template (value) charstring username,
75 template (value) charstring realm,
76 template (value) charstring nonce,
77 template (value) charstring uri,
78 template (value) charstring response,
79 template (value) charstring opaque,
80 template (value) charstring algorithm := "MD5",
81 template (value) charstring qop := "auth",
82 template (omit) charstring cnonce := omit,
83 template (omit) charstring nc := omit
84 ) := {
85 digestResponse := {
86 // Already added by digestResponse automatically:
87 //ts_Param("Digest", omit),
88 ts_Param("username", f_sip_str_quote(username)),
89 ts_Param("realm", f_sip_str_quote(realm)),
90 ts_Param("nonce", f_sip_str_quote(nonce)),
91 ts_Param("uri", f_sip_str_quote(uri)),
92 ts_Param("response", f_sip_str_quote(response)),
93 ts_Param("opaque", f_sip_str_quote(opaque)),
94 ts_Param("algorithm", algorithm),
95 ts_Param("qop", qop),
96 // FIXME: If "omit" is passed, these below end up in;
97 // "Dynamic test case error: Performing a valueof or send operation on a non-specific template of type @SIPmsg_Types.GenericParam"
98 f_ts_Param_omit("cnonce", f_sip_str_quote(cnonce)),
99 f_ts_Param_omit("nc", nc)
100 }
101}
102
103template (value) Credentials ts_Credentials_OtherAuth(template (value) OtherAuth otherResponse) := {
104 otherResponse := otherResponse
105}
106
107template (value) Authorization ts_Authorization(template (value) Credentials body) := {
108 fieldName := AUTHORIZATION_E,
109 body := body
110}
111
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100112// [20.10]
113template (present) NameAddr tr_NameAddr(template (present) SipUrl addrSpec := ?,
114 template charstring displayName := *) := {
115 displayName := displayName,
116 addrSpec := addrSpec
Harald Welteb0d93602018-03-20 18:09:34 +0100117}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100118template (value) NameAddr ts_NameAddr(template (value) SipUrl addrSpec,
119 template (omit) charstring displayName := omit) := {
120 displayName := displayName,
121 addrSpec := addrSpec
122}
123
124template (present) Addr_Union tr_Addr_Union_NameAddr(template (present) NameAddr nameAddr := ?) := {
125 nameAddr := nameAddr
126}
127template (value) Addr_Union ts_Addr_Union_NameAddr(template (value) NameAddr nameAddr) := {
128 nameAddr := nameAddr
129}
130
131template (present) Addr_Union tr_Addr_Union_SipUrl(template (present) SipUrl sipUrl := ?) := {
132 addrSpecUnion := sipUrl
133}
134template (value) Addr_Union ts_Addr_Union_SipUrl(template (value) SipUrl sipUrl) := {
135 addrSpecUnion := sipUrl
136}
137
138
139template (present) ContactAddress tr_ContactAddress(template (present) Addr_Union addressField := ?,
140 template SemicolonParam_List contactParams := *) := {
141 addressField := addressField,
142 contactParams := contactParams
143}
144template (value) ContactAddress ts_ContactAddress(template (value) Addr_Union addressField,
145 template (omit) SemicolonParam_List contactParams := omit) := {
146 addressField := addressField,
147 contactParams := contactParams
148}
149
150template (present) Contact tr_Contact(template (present) ContactAddress_List contactAddresses := ?) := {
151 fieldName := CONTACT_E,
152 contactBody := {
153 contactAddresses := contactAddresses
154 }
155}
156template (value) Contact ts_Contact(template (value) ContactAddress_List contactAddresses) := {
157 fieldName := CONTACT_E,
158 contactBody := {
159 contactAddresses := contactAddresses
160 }
161}
162
163template (value) Contact ts_ContactWildcard := {
164 fieldName := CONTACT_E,
165 contactBody := {
166 wildcard := "*"
167 }
168}
169
170template (present) Contact tr_Contact_SipAddr(template (present) SipAddr contact_addr := ?)
171 := tr_Contact({ tr_ContactAddress(contact_addr.addr, contact_addr.params) });
172
173private function f_tr_Contact_SipAddr(template SipAddr contact_addr) return template Contact
174{
175 if (istemplatekind(contact_addr, "omit")) {
176 return omit;
177 } else if (istemplatekind(contact_addr, "*")) {
178 return *;
179 }
180 return tr_Contact_SipAddr(contact_addr);
181}
182
183template (value) Contact ts_Contact_SipAddr(template (value) SipAddr contact_addr)
184 := ts_Contact({ ts_ContactAddress(contact_addr.addr, contact_addr.params) });
185private function ts_Contact_SipAddr_omit(template (omit) SipAddr contact_addr := omit) return template (omit) Contact
186{
187 if (istemplatekind(contact_addr, "omit")) {
188 return omit;
189 }
190 return ts_Contact_SipAddr(contact_addr);
191}
192
193
194// [20.19]
195template (value) Expires ts_Expires(template (value) DeltaSec deltaSec := "7200") := {
196 fieldName := EXPIRES_E,
197 deltaSec := deltaSec
198}
199
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200200
201// [20.32]
202template (value) Require ts_Require(template (value) OptionTag_List optionsTags := {}) := {
203 fieldName := REQUIRE_E,
204 optionsTags := optionsTags
205}
206template (present) Require tr_Require(template (present) OptionTag_List optionsTags := ?) := {
207 fieldName := REQUIRE_E,
208 optionsTags := optionsTags
209}
210
211// [20.37]
212template (value) Supported ts_Supported(template (value) OptionTag_List optionsTags := {}) := {
213 fieldName := SUPPORTED_E,
214 optionsTags := optionsTags
215}
216template (present) Supported tr_Supported(template (present) OptionTag_List optionsTags := ?) := {
217 fieldName := SUPPORTED_E,
218 optionsTags := optionsTags
219}
220
221
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100222template (value) SipAddr ts_SipAddr(template (value) HostPort host_port,
223 template (omit) UserInfo user_info := omit,
224 template (omit) charstring displayName := omit,
225 template (omit) SemicolonParam_List params := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100226 addr := {
227 nameAddr := {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100228 displayName := displayName,
229 addrSpec := ts_SipUrl(host_port, user_info)
Harald Welteb0d93602018-03-20 18:09:34 +0100230 }
231 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100232 params := params
233}
234template (present) SipAddr tr_SipAddr(template (present) HostPort host_port := ?,
235 template UserInfo user_info := *,
236 template charstring displayName := *,
237 template SemicolonParam_List params := *) := {
238 addr := {
239 nameAddr := {
240 displayName := displayName,
241 addrSpec := tr_SipUrl(host_port, user_info)
242 }
243 },
244 params := params
Harald Welteb0d93602018-03-20 18:09:34 +0100245}
246
247/* build a receive template from a value: substitute '*' for omit */
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200248function tr_SipUrl_from_val(template (value) SipUrl tin) return template (present) SipUrl {
249 var template (present) SipUrl ret := tin;
250
251 /* if the port number is 5060, it may be omitted */
252 if (ispresent(tin.hostPort.portField) and
253 valueof(tin.hostPort.portField) == 5060) {
254 ret.hostPort.portField := 5060 ifpresent;
255 }
256 if (not ispresent(tin.userInfo.password)) {
257 ret.userInfo.password := *;
258 }
259
260 return ret;
261}
262function tr_SipAddr_from_val(template (value) SipAddr tin) return template (present) SipAddr {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100263 var template (present) SipAddr ret := tin;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200264
265 if (not ispresent(tin.addr.nameAddr.displayName)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100266 ret.addr.nameAddr.displayName := *;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200267 } else if (f_str_tolower(f_sip_str_unquote(tin.addr.nameAddr.displayName)) == "anonymous") {
268 /* if the user is Anonymous, it may be omitted */
269 ret.addr.nameAddr.displayName := tin.addr.nameAddr.displayName ifpresent;
Harald Welteb0d93602018-03-20 18:09:34 +0100270 }
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200271
272 ret.addr.nameAddr.addrSpec := tr_SipUrl_from_val(tin.addr.nameAddr.addrSpec);
273
274 if (not ispresent(tin.params)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100275 ret.params := *;
276 }
277 return ret;
278}
279
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200280function ts_SipAddr_from_Addr_Union(template (value) Addr_Union au,
281 template (omit) SemicolonParam_List params := omit)
282return template (value) SipAddr {
283 var template (value) SipUrl addrSpec := ts_SipUrl_from_Addr_Union(au);
284 var template (omit) charstring displayName;
285
286 if (ischosen(au.nameAddr)) {
287 displayName := au.nameAddr.displayName;
288 } else { /* au.addrSpecUnion */
289 displayName := omit
290 }
291
292 return ts_SipAddr(addrSpec.hostPort,
293 addrSpec.userInfo,
294 displayName,
295 params);
296}
297
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100298template (value) HostPort ts_HostPort(template (omit) charstring host := omit,
299 template (omit) integer portField := omit) := {
300 host := host,
301 portField := portField
302}
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200303
304template (present) HostPort tr_HostPort(template charstring host := *,
305 template integer portField := *) := {
306 host := host,
307 portField := portField
308}
309function f_tr_HostPort(template charstring host := *,
310 template integer portField := *)
311return template (present) HostPort {
312 return f_tr_HostPort_opt_defport(tr_HostPort(host, portField));
313}
314function f_tr_HostPort_opt_defport(template (present) HostPort hp) return template (present) HostPort {
315 var template (present) HostPort hpout := hp;
Harald Welteb0d93602018-03-20 18:09:34 +0100316 /* if the port number is 5060, it may be omitted */
317 if (isvalue(hp.portField) and valueof(hp.portField) == 5060) {
318 hpout.portField := 5060 ifpresent;
319 }
320 return hpout;
321}
322
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200323function f_tr_SipUrl_opt_defport(template (present) SipUrl url) return template (present) SipUrl {
324 var template (present) SipUrl urlout := url;
325 urlout.hostPort := f_tr_HostPort_opt_defport(url.hostPort);
326 return urlout;
327}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100328
329template (value) UserInfo ts_UserInfo(template (value) charstring userOrTelephoneSubscriber,
330 template (omit) charstring password := omit) := {
331 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
332 password := password
333}
334template (present) UserInfo tr_UserInfo(template (present) charstring userOrTelephoneSubscriber := ?,
335 template charstring password := *) := {
336 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
337 password := password
338}
339
340template (value) RequestLine ts_SIP_ReqLine(Method method,
341 template (value) SipUrl uri,
Harald Welteb0d93602018-03-20 18:09:34 +0100342 charstring ver := c_SIP_VERSION) := {
343 method := method,
344 requestUri := uri,
345 sipVersion := ver
346}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100347template (present) RequestLine tr_SIP_ReqLine(template (present) Method method := ?,
348 template (present) SipUrl uri := ?,
349 template (present) charstring ver := c_SIP_VERSION) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100350 method := method,
351 requestUri := uri,
352 sipVersion := ver
353}
354
355template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := {
356 sipVersion := "SIP/2.0",
357 statusCode := status_code,
358 reasonPhrase := reason
359}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100360template (present) StatusLine tr_SIP_StatusLine(template integer status_code,
361 template charstring reason) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100362 sipVersion := "SIP/2.0",
363 statusCode := status_code,
364 reasonPhrase := reason
365}
366
367
368template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := {
369 requestLine := rl,
370 msgHeader := c_SIP_msgHeader_empty,
371 messageBody := omit,
372 payload := omit
373}
374
375const Method_List c_SIP_defaultMethods := {
376 "INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE",
377 "NOTIFY", "REFER", "UPDATE" };
378
379private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body)
380return template (omit) ContentType {
381 /* if user explicitly stated no content type */
382 if (istemplatekind(ct, "omit")) {
383 return omit;
384 }
385 /* if there's no body, then there's no content-type either */
386 if (istemplatekind(body, "omit")) {
387 return omit;
388 }
389 return ct;
390}
391
392template (value) ContentType ts_CT_SDP := {
393 fieldName := CONTENT_TYPE_E,
394 mediaType := "application/sdp"
395};
396
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200397template (value) Via ts_Via_from(template (value) HostPort addr,
398 template (value) charstring transport := "UDP") := {
Harald Welteb0d93602018-03-20 18:09:34 +0100399 fieldName := VIA_E,
400 viaBody := {
401 {
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200402 sentProtocol := { "SIP", "2.0", transport },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100403 sentBy := addr,
Harald Welteb0d93602018-03-20 18:09:34 +0100404 viaParams := omit
405 }
406 }
407}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100408template (present) Via tr_Via_from(template (present) HostPort host_port := ?,
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200409 template (present) charstring transport := ?,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100410 template SemicolonParam_List viaParams := *) := {
411 fieldName := VIA_E,
412 viaBody := {
413 {
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200414 sentProtocol := { "SIP", "2.0", ? },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100415 sentBy := host_port,
416 viaParams := viaParams
417 }
418 }
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200419}
420
421template (present) OtherAuth
422tr_OtherAuth(template (present) charstring authScheme := ?,
423 template (present) CommaParam_List authParams := ?) := {
424 authScheme := authScheme,
425 authParams := authParams
426}
427
428template (value) OtherAuth
429ts_OtherAuth(template (value) charstring authScheme,
430 template (value) CommaParam_List authParams) := {
431 authScheme := authScheme,
432 authParams := authParams
433}
434
435template (present) Challenge
436tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := {
437 digestCln := digestCln
438}
439
440template (value) Challenge
441ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := {
442 digestCln := digestCln
443}
444
445template (present) Challenge
446tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := {
447 otherChallenge := otherChallenge
448}
449
450template (value) Challenge
451ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := {
452 otherChallenge := otherChallenge
453}
454
455template (present) WwwAuthenticate
456tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := {
457 fieldName := WWW_AUTHENTICATE_E,
458 challenge := challenge
459}
460
461template (value) WwwAuthenticate
462ts_WwwAuthenticate(template (value) Challenge_list challenge) := {
463 fieldName := WWW_AUTHENTICATE_E,
464 challenge := challenge
465}
Harald Welteb0d93602018-03-20 18:09:34 +0100466
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200467// RFC3329
468template (present) Security_client
469tr_Security_client(template (present) Security_mechanism_list sec_mechanism_list := ?) := {
470 fieldName := SECURITY_CLIENT_E,
471 sec_mechanism_list := sec_mechanism_list
472}
473template (value) Security_client
474ts_Security_client(template (value) Security_mechanism_list sec_mechanism_list) := {
475 fieldName := SECURITY_CLIENT_E,
476 sec_mechanism_list := sec_mechanism_list
477}
478
Pau Espin Pedrol7f411562024-05-13 13:58:41 +0200479template (present) Security_server
480tr_Security_server(template (present) Security_mechanism_list sec_mechanism_list := ?) := {
481 fieldName := SECURITY_SERVER_E,
482 sec_mechanism_list := sec_mechanism_list
483}
484template (value) Security_server
485ts_Security_server(template (value) Security_mechanism_list sec_mechanism_list) := {
486 fieldName := SECURITY_SERVER_E,
487 sec_mechanism_list := sec_mechanism_list
488}
489
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200490template (present) Security_mechanism
491tr_Security_mechanism(template (present) charstring name := ?,
492 template SemicolonParam_List params := *) := {
493 mechanism_name := name,
494 mechanism_params := params
495}
496template (value) Security_mechanism
497ts_Security_mechanism(template (value) charstring name,
498 template (omit) SemicolonParam_List params := omit) := {
499 mechanism_name := name,
500 mechanism_params := params
501}
502
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100503template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
504template (value) MessageHeader
505ts_SIP_msgh_std(template (value) CallidString call_id,
506 template (value) SipAddr from_addr,
507 template (value) SipAddr to_addr,
508 template (omit) Contact contact,
509 template (value) charstring method,
510 template (value) integer seq_nr,
511 template (value) Via via,
512 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200513 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100514 template (value) Method_List allow_methods := c_SIP_defaultMethods,
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200515 template (omit) Expires expires := omit,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200516 template (omit) Require require := omit,
517 template (omit) Security_client security_client := omit,
Pau Espin Pedrol7f411562024-05-13 13:58:41 +0200518 template (omit) Security_server security_server := omit,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200519 template (omit) Supported supported := omit,
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200520 template (omit) WwwAuthenticate wwwAuthenticate := omit
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100521 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100522 allow := {
523 fieldName := ALLOW_E,
524 methods := allow_methods
525 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200526 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100527 callId := {
528 fieldName := CALL_ID_E,
529 callid := call_id
530 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100531 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100532 contentType := content_type,
533 cSeq := {
534 fieldName := CSEQ_E,
535 seqNumber := seq_nr,
536 method := method
537 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100538 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100539 fromField := {
540 fieldName := FROM_E,
541 addressField := from_addr.addr,
542 fromParams := from_addr.params
543 },
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200544 require := require,
545 security_client := security_client,
Pau Espin Pedrol7f411562024-05-13 13:58:41 +0200546 security_server := security_server,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200547 supported := supported,
Harald Welteb0d93602018-03-20 18:09:34 +0100548 toField := {
549 fieldName := TO_E,
550 addressField := to_addr.addr,
551 toParams := to_addr.params
552 },
553 userAgent := {
554 fieldName := USER_AGENT_E,
555 userAgentBody := {
556 "osmo-ttcn3-hacks/0.23"
557 }
558 },
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200559 via := via,
560 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100561}
562
Harald Welteb0d93602018-03-20 18:09:34 +0100563function tr_AllowMethods(template Method_List allow_methods) return template Allow {
564 if (istemplatekind(allow_methods, "omit")) {
565 return omit;
566 } else if (istemplatekind(allow_methods, "*")) {
567 return *;
568 } else if (istemplatekind(allow_methods, "?")) {
569 return ?;
570 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100571 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100572 fieldName := ALLOW_E,
573 methods := allow_methods
574 }
575 return ret
576}
577
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100578template (present) MessageHeader
579tr_SIP_msgh_std(template CallidString call_id,
580 template SipAddr from_addr,
581 template SipAddr to_addr,
582 template Contact contact,
583 template (present) Via via := tr_Via_from(?),
584 template charstring method,
585 template ContentType content_type := *,
586 template integer seq_nr := ?,
587 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200588 template Expires expires := *,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200589 template Require require := *,
590 template Security_client security_client := *,
Pau Espin Pedrol7f411562024-05-13 13:58:41 +0200591 template Security_server security_server := *,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200592 template Supported supported := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200593 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100594 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100595 allow := tr_AllowMethods(allow_methods),
596 callId := {
597 fieldName := CALL_ID_E,
598 callid := call_id
599 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100600 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100601 contentType := content_type,
602 cSeq := {
603 fieldName := CSEQ_E,
604 seqNumber := seq_nr,
605 method := method
606 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100607 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100608 fromField := {
609 fieldName := FROM_E,
610 addressField := from_addr.addr,
611 fromParams := from_addr.params
612 },
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200613 require := require,
614 security_client := security_client,
Pau Espin Pedrol7f411562024-05-13 13:58:41 +0200615 security_server := security_server,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200616 supported := supported,
Harald Welteb0d93602018-03-20 18:09:34 +0100617 toField := {
618 fieldName := TO_E,
619 addressField := to_addr.addr,
620 toParams := to_addr.params
621 },
622 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200623 via := via,
624 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100625}
626
627
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100628template (value) PDU_SIP_Request
629ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
630 template (value) CallidString call_id,
631 template (value) SipAddr from_addr,
632 template (value) SipAddr to_addr,
633 template (value) Via via,
634 integer seq_nr,
635 template (omit) Contact contact,
636 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200637 template (omit) Authorization authorization := omit,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200638 template (omit) Require require := omit,
639 template (omit) Security_client security_client := omit,
640 template (omit) Supported supported := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100641 template (omit) charstring body := omit) := {
642 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
643 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
644 "REGISTER", seq_nr, via,
645 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200646 authorization := authorization,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200647 expires := expires,
648 require := require,
649 security_client := security_client,
650 supported := supported),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100651 messageBody := body,
652 payload := omit
653}
654template (present) PDU_SIP_Request
655tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
656 template (present) CallidString call_id := ?,
657 template (present) SipAddr from_addr := ?,
658 template (present) SipAddr to_addr := ?,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200659 template (present) Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100660 template integer seq_nr := *,
661 template Contact contact := *,
662 template Expires expires := *,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200663 template Require require := *,
664 template Security_client security_client := *,
665 template Supported supported := *,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100666 template charstring body := *) := {
667 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
668 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200669 via, "REGISTER", *, seq_nr,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200670 expires := expires,
671 require := require,
672 security_client := security_client,
673 supported := supported),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100674 messageBody := body,
675 payload := omit
676}
677
678template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200679ts_SIP_INVITE(template (value) CallidString call_id,
680 template (value) SipAddr from_addr,
681 template (value) SipAddr to_addr,
682 template (value) Via via,
683 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100684 integer seq_nr,
685 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100686 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200687 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100688 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200689 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100690 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100691 messageBody := body,
692 payload := omit
693}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100694template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200695tr_SIP_INVITE(template (present) SipUrl uri,
696 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100697 template SipAddr from_addr,
698 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200699 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100700 template integer seq_nr,
701 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200702 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100703 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200704 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100705 messageBody := body,
706 payload := omit
707}
708
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100709template (value) PDU_SIP_Request
710ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200711 template (value) SipAddr from_addr,
712 template (value) SipAddr to_addr,
713 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100714 integer seq_nr,
715 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100716 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
717 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200718 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100719 messageBody := body,
720 payload := omit
721}
722
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100723template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200724tr_SIP_BYE(template (present) SipUrl uri,
725 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100726 template SipAddr from_addr,
727 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200728 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100729 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200730 template charstring body := *) := {
731 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100732 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200733 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100734 messageBody := body,
735 payload := omit
736}
737
738
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100739template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200740ts_SIP_ACK(template (value) CallidString call_id,
741 template (value) SipAddr from_addr,
742 template (value) SipAddr to_addr,
743 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100744 integer seq_nr,
745 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100746 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100747 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
748 ts_Contact_SipAddr(from_addr),
749 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200750 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100751 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100752 messageBody := body,
753 payload := omit
754}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100755template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200756tr_SIP_ACK(template (present) SipUrl uri,
757 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100758 template SipAddr from_addr,
759 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200760 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100761 template integer seq_nr,
762 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200763 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100764 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200765 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100766 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100767 messageBody := body,
768 payload := omit
769}
770
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200771template (present) PDU_SIP_Request
772tr_SIP_CANCEL(template (present) SipUrl uri,
773 template (present) CallidString call_id,
774 template (present) SipAddr from_addr,
775 template (present) SipAddr to_addr,
776 template (present) Via via,
777 template (present) integer seq_nr,
778 template charstring body := *) := {
779 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
780 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
781 via,
782 "CANCEL", *, seq_nr),
783 messageBody := body,
784 payload := omit
785}
786
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100787template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200788ts_SIP_Response(template (value) CallidString call_id,
789 template (value) SipAddr from_addr,
790 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100791 charstring method,
792 integer status_code,
793 integer seq_nr,
794 charstring reason,
795 Via via,
796 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100797 statusLine := ts_SIP_StatusLine(status_code, reason),
798 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
799 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
800 messageBody := body,
801 payload := omit
802}
803
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200804/* 180 Ringing */
805template (value) PDU_SIP_Response
806ts_SIP_Response_Ringing(
807 template (value) CallidString call_id,
808 template (value) SipAddr from_addr,
809 template (value) SipAddr to_addr,
810 Via via,
811 integer seq_nr,
812 charstring method := "INVITE",
813 template (omit) charstring body := omit) := {
814 statusLine := ts_SIP_StatusLine(180, "Ringing"),
815 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
816 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
817 messageBody := body,
818 payload := omit
819}
820
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100821template (present) PDU_SIP_Response
822tr_SIP_Response(template CallidString call_id,
823 template SipAddr from_addr,
824 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200825 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100826 template Contact contact,
827 template charstring method,
828 template integer status_code,
829 template integer seq_nr := ?,
830 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200831 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100832 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100833 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200834 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100835 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100836 messageBody := body,
837 payload := omit
838}
839
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200840/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100841template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200842tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100843 template CallidString call_id,
844 template SipAddr from_addr,
845 template SipAddr to_addr,
846 template (present) Via via := tr_Via_from(?),
847 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200848 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100849 template integer seq_nr := ?,
850 template charstring method := "REGISTER",
851 template integer status_code := 401,
852 template charstring reason := "Unauthorized",
853 template charstring body := *) := {
854 statusLine := tr_SIP_StatusLine(status_code, reason),
855 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
856 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200857 method, *, seq_nr,
858 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100859 messageBody := body,
860 payload := omit
861}
Harald Welteb0d93602018-03-20 18:09:34 +0100862
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200863/* 100 Trying */
864template (present) PDU_SIP_Response
865tr_SIP_Response_Trying(
866 template CallidString call_id,
867 template SipAddr from_addr,
868 template SipAddr to_addr,
869 template (present) Via via := tr_Via_from(?),
870 template integer seq_nr := ?,
871 template charstring method := "INVITE",
872 template integer status_code := 100,
873 template charstring reason := "Trying",
874 template charstring body := *) := {
875 statusLine := tr_SIP_StatusLine(status_code, reason),
876 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
877 via,
878 method, *, seq_nr),
879 messageBody := body,
880 payload := omit
881}
882
883/* 180 Ringing */
884template (present) PDU_SIP_Response
885tr_SIP_Response_Ringing(
886 template CallidString call_id,
887 template SipAddr from_addr,
888 template SipAddr to_addr,
889 template (present) Via via := tr_Via_from(?),
890 template integer seq_nr := ?,
891 template charstring method := "INVITE",
892 template integer status_code := 180,
893 template charstring reason := "Ringing",
894 template charstring body := *) := {
895 statusLine := tr_SIP_StatusLine(status_code, reason),
896 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
897 via,
898 method, *, seq_nr),
899 messageBody := body,
900 payload := omit
901}
902
903/****************
904 * FUNCTIONS:
905 ****************/
906
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200907function f_sip_param_find(GenericParam_List li,
908 template (present) charstring id := ?)
909return template (omit) GenericParam {
910 var integer i;
911
912 for (i := 0; i < lengthof(li); i := i + 1) {
913 if (not ispresent(li[i])) {
914 continue;
915 }
916 if (match(li[i].id, id)) {
917 return li[i];
918 }
919 }
920 return omit;
921}
922
923function f_sip_param_find_or_fail(GenericParam_List li,
924 template (present) charstring id := ?)
925return GenericParam {
926 var template (omit) GenericParam parameter;
927 parameter := f_sip_param_find(li, id);
928 if (istemplatekind(parameter, "omit")) {
929 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
930 log2str("Param ", id, " not found in ", li));
931 }
932 return valueof(parameter);
933}
934
935function f_sip_param_get_value(GenericParam_List li,
936 template (present) charstring id := ?)
937return template (omit) charstring {
938 var template (omit) GenericParam parameter;
939 parameter := f_sip_param_find(li, id);
940 if (istemplatekind(parameter, "omit")) {
941 return omit;
942 }
943 return parameter.paramValue;
944}
945
946function f_sip_param_get_value_or_fail(GenericParam_List li,
947 template (present) charstring id := ?)
948return template (omit) charstring {
949 var GenericParam parameter;
950 parameter := f_sip_param_find_or_fail(li, id);
951 return parameter.paramValue;
952}
953
954function f_sip_param_get_value_present_or_fail(GenericParam_List li,
955 template (present) charstring id := ?)
956return charstring {
957 var GenericParam parameter;
958 parameter := f_sip_param_find_or_fail(li, id);
959 if (not ispresent(parameter.paramValue)) {
960 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
961 log2str("Param ", id, " value not present in ", li));
962 }
963 return parameter.paramValue;
964}
965
966function f_sip_param_match_value(GenericParam_List li,
967 template (present) charstring id := ?,
968 template charstring exp_paramValue := *)
969return boolean {
970 var template (omit) charstring val;
971 val := f_sip_param_get_value_or_fail(li, id);
972 if (istemplatekind(val, "omit")) {
973 return istemplatekind(val, "omit") or istemplatekind(val, "*");
974 }
975 return match(valueof(val), exp_paramValue);
976}
977
978function f_sip_param_match_value_or_fail(GenericParam_List li,
979 template (present) charstring id := ?,
980 template charstring exp_paramValue := *)
981{
982 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
983 if (istemplatekind(val, "omit")) {
984 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
985 return;
986 } else {
987 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
988 log2str("Param ", id, " match failed: val ", val,
989 " vs exp ", exp_paramValue));
990 }
991 }
992 if (not match(valueof(val), exp_paramValue)) {
993 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
994 log2str("Param ", id, " match failed: val ", val,
995 " vs exp ", exp_paramValue));
996 }
997}
998
999function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
1000return GenericParam_List {
1001 var integer i;
1002 var GenericParam_List li;
1003 var GenericParam_List new_li := {};
1004
1005 if (istemplatekind(li_tpl, "omit")) {
1006 return {};
1007 }
1008
1009 li := valueof(li_tpl);
1010 for (i := 0; i < lengthof(li); i := i + 1) {
1011 if (not ispresent(li[i]) or
1012 not match(li[i].id, id)) {
1013 new_li := new_li & {li[i]};
1014 }
1015 }
1016 return new_li;
1017}
1018
1019function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
1020return GenericParam_List {
1021 var integer i;
1022 var GenericParam_List li;
1023 var GenericParam_List new_li := {};
1024 var boolean found := false;
1025
1026 if (istemplatekind(li_tpl, "omit")) {
1027 return { valueof(ts_Param(id, val)) };
1028 }
1029
1030 li := valueof(li_tpl);
1031 for (i := 0; i < lengthof(li); i := i + 1) {
1032 if (not ispresent(li[i]) or
1033 not match(li[i].id, id)) {
1034 new_li := new_li & {li[i]};
1035 continue;
1036 }
1037 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
1038 found := true;
1039 }
1040
1041 if (not found) {
1042 new_li := new_li & { valueof(ts_Param(id, val)) };
1043 }
1044 return new_li;
1045}
1046
1047/* Make sure string is quoted. */
1048function f_sip_str_quote(template (value) charstring val) return charstring {
1049 var charstring str := valueof(val);
1050 if (lengthof(str) == 0) {
1051 return "";
1052 }
1053
1054 if (str[0] != "\"") {
1055 return "\"" & str & "\"";
1056 }
1057 return str;
1058}
1059
1060/* Make sure string is unquoted.
1061 * Similar to unq() in RFC 2617 */
1062function f_sip_str_unquote(template (value) charstring val) return charstring {
1063 var charstring str := valueof(val);
1064 var integer len := lengthof(str);
1065
1066 if (len <= 1) {
1067 return str;
1068 }
1069
1070 if (str[0] == "\"" and str[len - 1] == "\"") {
1071 return substr(str, 1, len - 2);
1072 }
1073 return str;
1074}
1075
1076/* RFC 2617 3.2.2.2 A1 */
1077function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
1078
1079 /* RFC 2617 3.2.2.2 A1 */
1080 var charstring A1 := f_sip_str_unquote(user) & ":" &
1081 f_sip_str_unquote(realm) & ":" &
1082 password;
1083 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
1084 log("A1: md5('", A1, "') = ", digestA1);
1085 return digestA1;
1086}
1087
1088/* RFC 2617 3.2.2.2 A2 */
1089function f_sip_digest_A2(charstring method, charstring uri) return charstring {
1090
1091 var charstring A2 := method & ":" & uri
1092 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1093 log("A2: md5('", A2, "') = ", digestA2);
1094 return digestA2;
1095}
1096
1097/* RFC 2617 3.2.2.1 Request-Digest */
1098function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1099 charstring nc, charstring cnonce,
1100 charstring qop, charstring digestA2) return charstring {
1101 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1102 nc & ":" &
1103 cnonce & ":" &
1104 f_sip_str_unquote(qop) & ":" &
1105 digestA2;
1106 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1107 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1108 return req_digest;
1109}
1110
1111/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1112 * KD(secret, data) = H(concat(secret, ":", data))
1113 */
1114function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1115 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1116}
1117
1118/* Digest Auth: RFC 2617 */
1119function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1120 charstring user, charstring password,
1121 charstring method, charstring uri,
1122 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1123 var CommaParam_List digestCln;
1124 var template (value) Authorization authorization;
1125 var template (value) Credentials cred;
1126 var template (omit) GenericParam rx_param;
1127
1128 digestCln := www_authenticate.challenge[0].digestCln;
1129
1130 var charstring algorithm;
1131 rx_param := f_sip_param_find(digestCln, "algorithm");
1132 if (istemplatekind(rx_param, "omit")) {
1133 /* Assume MD5 if not set */
1134 algorithm := "MD5"
1135 } else {
1136 algorithm := valueof(rx_param.paramValue);
1137 if (f_strstr(algorithm, "MD5") == -1) {
1138 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1139 log2str("Unexpected algorithm: ", algorithm));
1140 }
1141 }
1142
1143 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1144 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1145 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1146 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1147
1148 if (f_strstr(qop, "auth") == -1) {
1149 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1150 }
1151 var charstring selected_qop := "auth";
1152
1153 /* RFC 2617 3.2.2.2 A1 */
1154 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1155 /* RFC 2617 3.2.2.3 A2 */
1156 var charstring digestA2 := f_sip_digest_A2(method, uri);
1157
1158 /* RFC 2617 3.2.2.1 Request-Digest */
1159 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1160 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1161 nc, cnonce,
1162 selected_qop, digestA2);
1163
1164 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1165 uri, req_digest,
1166 opaque, algorithm, selected_qop, cnonce, nc);
1167
1168 authorization := ts_Authorization(cred);
1169 return valueof(authorization);
1170}
1171
1172/* RFC 2617 3.5 Example */
1173function f_sip_digest_selftest() {
1174/*
1175The following example assumes that an access-protected document is
1176being requested from the server via a GET request. The URI of the
1177document is "http://www.nowhere.org/dir/index.html". Both client and
1178server know that the username for this document is "Mufasa", and the
1179password is "Circle Of Life" (with one space between each of the
1180three words).
1181
1182HTTP/1.1 401 Unauthorized
1183WWW-Authenticate: Digest
1184 realm="testrealm@host.com",
1185 qop="auth,auth-int",
1186 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1187 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1188
1189Authorization: Digest username="Mufasa",
1190 realm="testrealm@host.com",
1191 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1192 uri="/dir/index.html",
1193 qop=auth,
1194 nc=00000001,
1195 cnonce="0a4f113b",
1196 response="6629fae49393a05397450978507c4ef1",
1197 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1198*/
1199 var template (value) CommaParam_List digestCln := {
1200 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1201 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1202 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1203 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1204 };
1205 var template (value) WwwAuthenticate www_authenticate :=
1206 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1207
1208 var Authorization authorization :=
1209 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1210 "Mufasa",
1211 "Circle Of Life",
1212 "GET",
1213 "/dir/index.html",
1214 cnonce := "0a4f113b",
1215 nc_int := 1);
1216
1217 var CommaParam_List digestResp := authorization.body.digestResponse;
1218 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1219 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1220 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1221 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1222 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1223 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1224 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1225 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1226}
1227
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001228/* RFC 3261 8.1.1.5:
1229 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1230 * and MUST be less than 2**31."
1231 */
1232function f_sip_rand_seq_nr() return integer {
1233 /* 2**31 = 2147483648 */
1234 return f_rnd_int(2147483648)
1235}
Harald Welteb0d93602018-03-20 18:09:34 +01001236
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001237function f_sip_next_seq_nr(integer seq_nr) return integer {
1238 return (seq_nr + 1) mod 2147483648;
1239}
1240
1241function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1242 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1243}
1244
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001245function f_sip_rand_tag() return charstring {
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001246 /* Tags shall have at least 32 bit of randomness */
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001247 var integer rnd_int := f_rnd_int(4294967296);
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001248 /* Make collisions harder by appending time to the final string: */
1249 var integer ts_int := f_time_ms() mod 4294967296;
1250 return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8));
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001251}
1252
1253/* Generate a "branch" tag value.
1254 * RFC 3261 p.105 section 8:
1255 * "A common way to create this value is to compute a
1256 * cryptographic hash of the To tag, From tag, Call-ID header
1257 * field, the Request-URI of the request received (before
1258 * translation), the topmost Via header, and the sequence number
1259 * from the CSeq header field, in addition to any Proxy-Require
1260 * and Proxy-Authorization header fields that may be present. The
1261 * algorithm used to compute the hash is implementation-dependent,
1262 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1263 * choice."
1264 * See also Section 8.1.1.7:
1265 * "The branch ID inserted by an element compliant with this
1266 * specification MUST always begin with the characters "z9hG4bK"."
1267 */
1268const charstring sip_magic_cookie := "z9hG4bK";
1269function f_sip_gen_branch(charstring tag_to,
1270 charstring tag_from,
1271 charstring tag_call_id,
1272 integer cseq) return charstring {
1273 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1274 var charstring hash := f_calculateMD5(str);
1275 var charstring branch := sip_magic_cookie & hash;
1276 return branch;
1277}
1278
1279function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1280 var charstring str := "";
1281 if (ispresent(host_port.host)) {
1282 str := host_port.host;
1283 }
1284 if (ispresent(host_port.portField)) {
1285 str := str & ":" & int2str(host_port.portField);
1286 }
1287 return str;
1288}
1289
1290function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1291 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1292 return str;
1293}
1294
1295function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1296 if (ispresent(naddr.displayName)) {
1297 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1298 } else {
1299 return f_sip_SipUrl_to_str(naddr.addrSpec);
1300 }
1301}
1302
1303function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1304 if (ischosen(sip_addr.addr.nameAddr)) {
1305 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1306 } else {
1307 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1308 }
1309}
1310
Harald Welteb0d93602018-03-20 18:09:34 +01001311}