blob: 7e33b3bcd87a63dafababde72f0e5397a2fd80fb [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
479template (present) Security_mechanism
480tr_Security_mechanism(template (present) charstring name := ?,
481 template SemicolonParam_List params := *) := {
482 mechanism_name := name,
483 mechanism_params := params
484}
485template (value) Security_mechanism
486ts_Security_mechanism(template (value) charstring name,
487 template (omit) SemicolonParam_List params := omit) := {
488 mechanism_name := name,
489 mechanism_params := params
490}
491
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100492template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
493template (value) MessageHeader
494ts_SIP_msgh_std(template (value) CallidString call_id,
495 template (value) SipAddr from_addr,
496 template (value) SipAddr to_addr,
497 template (omit) Contact contact,
498 template (value) charstring method,
499 template (value) integer seq_nr,
500 template (value) Via via,
501 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200502 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100503 template (value) Method_List allow_methods := c_SIP_defaultMethods,
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200504 template (omit) Expires expires := omit,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200505 template (omit) Require require := omit,
506 template (omit) Security_client security_client := omit,
507 template (omit) Supported supported := omit,
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200508 template (omit) WwwAuthenticate wwwAuthenticate := omit
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100509 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100510 allow := {
511 fieldName := ALLOW_E,
512 methods := allow_methods
513 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200514 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100515 callId := {
516 fieldName := CALL_ID_E,
517 callid := call_id
518 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100519 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100520 contentType := content_type,
521 cSeq := {
522 fieldName := CSEQ_E,
523 seqNumber := seq_nr,
524 method := method
525 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100526 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100527 fromField := {
528 fieldName := FROM_E,
529 addressField := from_addr.addr,
530 fromParams := from_addr.params
531 },
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200532 require := require,
533 security_client := security_client,
534 supported := supported,
Harald Welteb0d93602018-03-20 18:09:34 +0100535 toField := {
536 fieldName := TO_E,
537 addressField := to_addr.addr,
538 toParams := to_addr.params
539 },
540 userAgent := {
541 fieldName := USER_AGENT_E,
542 userAgentBody := {
543 "osmo-ttcn3-hacks/0.23"
544 }
545 },
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200546 via := via,
547 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100548}
549
Harald Welteb0d93602018-03-20 18:09:34 +0100550function tr_AllowMethods(template Method_List allow_methods) return template Allow {
551 if (istemplatekind(allow_methods, "omit")) {
552 return omit;
553 } else if (istemplatekind(allow_methods, "*")) {
554 return *;
555 } else if (istemplatekind(allow_methods, "?")) {
556 return ?;
557 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100558 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100559 fieldName := ALLOW_E,
560 methods := allow_methods
561 }
562 return ret
563}
564
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100565template (present) MessageHeader
566tr_SIP_msgh_std(template CallidString call_id,
567 template SipAddr from_addr,
568 template SipAddr to_addr,
569 template Contact contact,
570 template (present) Via via := tr_Via_from(?),
571 template charstring method,
572 template ContentType content_type := *,
573 template integer seq_nr := ?,
574 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200575 template Expires expires := *,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200576 template Require require := *,
577 template Security_client security_client := *,
578 template Supported supported := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200579 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100580 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100581 allow := tr_AllowMethods(allow_methods),
582 callId := {
583 fieldName := CALL_ID_E,
584 callid := call_id
585 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100586 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100587 contentType := content_type,
588 cSeq := {
589 fieldName := CSEQ_E,
590 seqNumber := seq_nr,
591 method := method
592 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100593 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100594 fromField := {
595 fieldName := FROM_E,
596 addressField := from_addr.addr,
597 fromParams := from_addr.params
598 },
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200599 require := require,
600 security_client := security_client,
601 supported := supported,
Harald Welteb0d93602018-03-20 18:09:34 +0100602 toField := {
603 fieldName := TO_E,
604 addressField := to_addr.addr,
605 toParams := to_addr.params
606 },
607 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200608 via := via,
609 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100610}
611
612
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100613template (value) PDU_SIP_Request
614ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
615 template (value) CallidString call_id,
616 template (value) SipAddr from_addr,
617 template (value) SipAddr to_addr,
618 template (value) Via via,
619 integer seq_nr,
620 template (omit) Contact contact,
621 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200622 template (omit) Authorization authorization := omit,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200623 template (omit) Require require := omit,
624 template (omit) Security_client security_client := omit,
625 template (omit) Supported supported := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100626 template (omit) charstring body := omit) := {
627 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
628 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
629 "REGISTER", seq_nr, via,
630 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200631 authorization := authorization,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200632 expires := expires,
633 require := require,
634 security_client := security_client,
635 supported := supported),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100636 messageBody := body,
637 payload := omit
638}
639template (present) PDU_SIP_Request
640tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
641 template (present) CallidString call_id := ?,
642 template (present) SipAddr from_addr := ?,
643 template (present) SipAddr to_addr := ?,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200644 template (present) Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100645 template integer seq_nr := *,
646 template Contact contact := *,
647 template Expires expires := *,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200648 template Require require := *,
649 template Security_client security_client := *,
650 template Supported supported := *,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100651 template charstring body := *) := {
652 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
653 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200654 via, "REGISTER", *, seq_nr,
Pau Espin Pedrol73f6a312024-05-10 20:30:16 +0200655 expires := expires,
656 require := require,
657 security_client := security_client,
658 supported := supported),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100659 messageBody := body,
660 payload := omit
661}
662
663template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200664ts_SIP_INVITE(template (value) CallidString call_id,
665 template (value) SipAddr from_addr,
666 template (value) SipAddr to_addr,
667 template (value) Via via,
668 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100669 integer seq_nr,
670 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100671 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200672 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100673 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200674 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100675 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100676 messageBody := body,
677 payload := omit
678}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100679template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200680tr_SIP_INVITE(template (present) SipUrl uri,
681 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100682 template SipAddr from_addr,
683 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200684 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100685 template integer seq_nr,
686 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200687 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100688 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200689 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100690 messageBody := body,
691 payload := omit
692}
693
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100694template (value) PDU_SIP_Request
695ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200696 template (value) SipAddr from_addr,
697 template (value) SipAddr to_addr,
698 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100699 integer seq_nr,
700 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100701 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
702 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200703 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100704 messageBody := body,
705 payload := omit
706}
707
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100708template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200709tr_SIP_BYE(template (present) SipUrl uri,
710 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100711 template SipAddr from_addr,
712 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200713 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100714 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200715 template charstring body := *) := {
716 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100717 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200718 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100719 messageBody := body,
720 payload := omit
721}
722
723
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100724template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200725ts_SIP_ACK(template (value) CallidString call_id,
726 template (value) SipAddr from_addr,
727 template (value) SipAddr to_addr,
728 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100729 integer seq_nr,
730 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100731 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100732 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
733 ts_Contact_SipAddr(from_addr),
734 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200735 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100736 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100737 messageBody := body,
738 payload := omit
739}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100740template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200741tr_SIP_ACK(template (present) SipUrl uri,
742 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100743 template SipAddr from_addr,
744 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200745 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100746 template integer seq_nr,
747 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200748 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100749 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200750 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100751 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100752 messageBody := body,
753 payload := omit
754}
755
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200756template (present) PDU_SIP_Request
757tr_SIP_CANCEL(template (present) SipUrl uri,
758 template (present) CallidString call_id,
759 template (present) SipAddr from_addr,
760 template (present) SipAddr to_addr,
761 template (present) Via via,
762 template (present) integer seq_nr,
763 template charstring body := *) := {
764 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
765 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
766 via,
767 "CANCEL", *, seq_nr),
768 messageBody := body,
769 payload := omit
770}
771
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100772template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200773ts_SIP_Response(template (value) CallidString call_id,
774 template (value) SipAddr from_addr,
775 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100776 charstring method,
777 integer status_code,
778 integer seq_nr,
779 charstring reason,
780 Via via,
781 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100782 statusLine := ts_SIP_StatusLine(status_code, reason),
783 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
784 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
785 messageBody := body,
786 payload := omit
787}
788
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200789/* 180 Ringing */
790template (value) PDU_SIP_Response
791ts_SIP_Response_Ringing(
792 template (value) CallidString call_id,
793 template (value) SipAddr from_addr,
794 template (value) SipAddr to_addr,
795 Via via,
796 integer seq_nr,
797 charstring method := "INVITE",
798 template (omit) charstring body := omit) := {
799 statusLine := ts_SIP_StatusLine(180, "Ringing"),
800 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
801 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
802 messageBody := body,
803 payload := omit
804}
805
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100806template (present) PDU_SIP_Response
807tr_SIP_Response(template CallidString call_id,
808 template SipAddr from_addr,
809 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200810 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100811 template Contact contact,
812 template charstring method,
813 template integer status_code,
814 template integer seq_nr := ?,
815 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200816 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100817 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100818 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200819 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100820 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100821 messageBody := body,
822 payload := omit
823}
824
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200825/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100826template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200827tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100828 template CallidString call_id,
829 template SipAddr from_addr,
830 template SipAddr to_addr,
831 template (present) Via via := tr_Via_from(?),
832 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200833 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100834 template integer seq_nr := ?,
835 template charstring method := "REGISTER",
836 template integer status_code := 401,
837 template charstring reason := "Unauthorized",
838 template charstring body := *) := {
839 statusLine := tr_SIP_StatusLine(status_code, reason),
840 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
841 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200842 method, *, seq_nr,
843 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100844 messageBody := body,
845 payload := omit
846}
Harald Welteb0d93602018-03-20 18:09:34 +0100847
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200848/* 100 Trying */
849template (present) PDU_SIP_Response
850tr_SIP_Response_Trying(
851 template CallidString call_id,
852 template SipAddr from_addr,
853 template SipAddr to_addr,
854 template (present) Via via := tr_Via_from(?),
855 template integer seq_nr := ?,
856 template charstring method := "INVITE",
857 template integer status_code := 100,
858 template charstring reason := "Trying",
859 template charstring body := *) := {
860 statusLine := tr_SIP_StatusLine(status_code, reason),
861 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
862 via,
863 method, *, seq_nr),
864 messageBody := body,
865 payload := omit
866}
867
868/* 180 Ringing */
869template (present) PDU_SIP_Response
870tr_SIP_Response_Ringing(
871 template CallidString call_id,
872 template SipAddr from_addr,
873 template SipAddr to_addr,
874 template (present) Via via := tr_Via_from(?),
875 template integer seq_nr := ?,
876 template charstring method := "INVITE",
877 template integer status_code := 180,
878 template charstring reason := "Ringing",
879 template charstring body := *) := {
880 statusLine := tr_SIP_StatusLine(status_code, reason),
881 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
882 via,
883 method, *, seq_nr),
884 messageBody := body,
885 payload := omit
886}
887
888/****************
889 * FUNCTIONS:
890 ****************/
891
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200892function f_sip_param_find(GenericParam_List li,
893 template (present) charstring id := ?)
894return template (omit) GenericParam {
895 var integer i;
896
897 for (i := 0; i < lengthof(li); i := i + 1) {
898 if (not ispresent(li[i])) {
899 continue;
900 }
901 if (match(li[i].id, id)) {
902 return li[i];
903 }
904 }
905 return omit;
906}
907
908function f_sip_param_find_or_fail(GenericParam_List li,
909 template (present) charstring id := ?)
910return GenericParam {
911 var template (omit) GenericParam parameter;
912 parameter := f_sip_param_find(li, id);
913 if (istemplatekind(parameter, "omit")) {
914 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
915 log2str("Param ", id, " not found in ", li));
916 }
917 return valueof(parameter);
918}
919
920function f_sip_param_get_value(GenericParam_List li,
921 template (present) charstring id := ?)
922return template (omit) charstring {
923 var template (omit) GenericParam parameter;
924 parameter := f_sip_param_find(li, id);
925 if (istemplatekind(parameter, "omit")) {
926 return omit;
927 }
928 return parameter.paramValue;
929}
930
931function f_sip_param_get_value_or_fail(GenericParam_List li,
932 template (present) charstring id := ?)
933return template (omit) charstring {
934 var GenericParam parameter;
935 parameter := f_sip_param_find_or_fail(li, id);
936 return parameter.paramValue;
937}
938
939function f_sip_param_get_value_present_or_fail(GenericParam_List li,
940 template (present) charstring id := ?)
941return charstring {
942 var GenericParam parameter;
943 parameter := f_sip_param_find_or_fail(li, id);
944 if (not ispresent(parameter.paramValue)) {
945 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
946 log2str("Param ", id, " value not present in ", li));
947 }
948 return parameter.paramValue;
949}
950
951function f_sip_param_match_value(GenericParam_List li,
952 template (present) charstring id := ?,
953 template charstring exp_paramValue := *)
954return boolean {
955 var template (omit) charstring val;
956 val := f_sip_param_get_value_or_fail(li, id);
957 if (istemplatekind(val, "omit")) {
958 return istemplatekind(val, "omit") or istemplatekind(val, "*");
959 }
960 return match(valueof(val), exp_paramValue);
961}
962
963function f_sip_param_match_value_or_fail(GenericParam_List li,
964 template (present) charstring id := ?,
965 template charstring exp_paramValue := *)
966{
967 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
968 if (istemplatekind(val, "omit")) {
969 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
970 return;
971 } else {
972 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
973 log2str("Param ", id, " match failed: val ", val,
974 " vs exp ", exp_paramValue));
975 }
976 }
977 if (not match(valueof(val), exp_paramValue)) {
978 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
979 log2str("Param ", id, " match failed: val ", val,
980 " vs exp ", exp_paramValue));
981 }
982}
983
984function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
985return GenericParam_List {
986 var integer i;
987 var GenericParam_List li;
988 var GenericParam_List new_li := {};
989
990 if (istemplatekind(li_tpl, "omit")) {
991 return {};
992 }
993
994 li := valueof(li_tpl);
995 for (i := 0; i < lengthof(li); i := i + 1) {
996 if (not ispresent(li[i]) or
997 not match(li[i].id, id)) {
998 new_li := new_li & {li[i]};
999 }
1000 }
1001 return new_li;
1002}
1003
1004function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
1005return GenericParam_List {
1006 var integer i;
1007 var GenericParam_List li;
1008 var GenericParam_List new_li := {};
1009 var boolean found := false;
1010
1011 if (istemplatekind(li_tpl, "omit")) {
1012 return { valueof(ts_Param(id, val)) };
1013 }
1014
1015 li := valueof(li_tpl);
1016 for (i := 0; i < lengthof(li); i := i + 1) {
1017 if (not ispresent(li[i]) or
1018 not match(li[i].id, id)) {
1019 new_li := new_li & {li[i]};
1020 continue;
1021 }
1022 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
1023 found := true;
1024 }
1025
1026 if (not found) {
1027 new_li := new_li & { valueof(ts_Param(id, val)) };
1028 }
1029 return new_li;
1030}
1031
1032/* Make sure string is quoted. */
1033function f_sip_str_quote(template (value) charstring val) return charstring {
1034 var charstring str := valueof(val);
1035 if (lengthof(str) == 0) {
1036 return "";
1037 }
1038
1039 if (str[0] != "\"") {
1040 return "\"" & str & "\"";
1041 }
1042 return str;
1043}
1044
1045/* Make sure string is unquoted.
1046 * Similar to unq() in RFC 2617 */
1047function f_sip_str_unquote(template (value) charstring val) return charstring {
1048 var charstring str := valueof(val);
1049 var integer len := lengthof(str);
1050
1051 if (len <= 1) {
1052 return str;
1053 }
1054
1055 if (str[0] == "\"" and str[len - 1] == "\"") {
1056 return substr(str, 1, len - 2);
1057 }
1058 return str;
1059}
1060
1061/* RFC 2617 3.2.2.2 A1 */
1062function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
1063
1064 /* RFC 2617 3.2.2.2 A1 */
1065 var charstring A1 := f_sip_str_unquote(user) & ":" &
1066 f_sip_str_unquote(realm) & ":" &
1067 password;
1068 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
1069 log("A1: md5('", A1, "') = ", digestA1);
1070 return digestA1;
1071}
1072
1073/* RFC 2617 3.2.2.2 A2 */
1074function f_sip_digest_A2(charstring method, charstring uri) return charstring {
1075
1076 var charstring A2 := method & ":" & uri
1077 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1078 log("A2: md5('", A2, "') = ", digestA2);
1079 return digestA2;
1080}
1081
1082/* RFC 2617 3.2.2.1 Request-Digest */
1083function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1084 charstring nc, charstring cnonce,
1085 charstring qop, charstring digestA2) return charstring {
1086 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1087 nc & ":" &
1088 cnonce & ":" &
1089 f_sip_str_unquote(qop) & ":" &
1090 digestA2;
1091 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1092 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1093 return req_digest;
1094}
1095
1096/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1097 * KD(secret, data) = H(concat(secret, ":", data))
1098 */
1099function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1100 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1101}
1102
1103/* Digest Auth: RFC 2617 */
1104function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1105 charstring user, charstring password,
1106 charstring method, charstring uri,
1107 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1108 var CommaParam_List digestCln;
1109 var template (value) Authorization authorization;
1110 var template (value) Credentials cred;
1111 var template (omit) GenericParam rx_param;
1112
1113 digestCln := www_authenticate.challenge[0].digestCln;
1114
1115 var charstring algorithm;
1116 rx_param := f_sip_param_find(digestCln, "algorithm");
1117 if (istemplatekind(rx_param, "omit")) {
1118 /* Assume MD5 if not set */
1119 algorithm := "MD5"
1120 } else {
1121 algorithm := valueof(rx_param.paramValue);
1122 if (f_strstr(algorithm, "MD5") == -1) {
1123 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1124 log2str("Unexpected algorithm: ", algorithm));
1125 }
1126 }
1127
1128 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1129 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1130 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1131 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1132
1133 if (f_strstr(qop, "auth") == -1) {
1134 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1135 }
1136 var charstring selected_qop := "auth";
1137
1138 /* RFC 2617 3.2.2.2 A1 */
1139 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1140 /* RFC 2617 3.2.2.3 A2 */
1141 var charstring digestA2 := f_sip_digest_A2(method, uri);
1142
1143 /* RFC 2617 3.2.2.1 Request-Digest */
1144 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1145 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1146 nc, cnonce,
1147 selected_qop, digestA2);
1148
1149 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1150 uri, req_digest,
1151 opaque, algorithm, selected_qop, cnonce, nc);
1152
1153 authorization := ts_Authorization(cred);
1154 return valueof(authorization);
1155}
1156
1157/* RFC 2617 3.5 Example */
1158function f_sip_digest_selftest() {
1159/*
1160The following example assumes that an access-protected document is
1161being requested from the server via a GET request. The URI of the
1162document is "http://www.nowhere.org/dir/index.html". Both client and
1163server know that the username for this document is "Mufasa", and the
1164password is "Circle Of Life" (with one space between each of the
1165three words).
1166
1167HTTP/1.1 401 Unauthorized
1168WWW-Authenticate: Digest
1169 realm="testrealm@host.com",
1170 qop="auth,auth-int",
1171 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1172 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1173
1174Authorization: Digest username="Mufasa",
1175 realm="testrealm@host.com",
1176 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1177 uri="/dir/index.html",
1178 qop=auth,
1179 nc=00000001,
1180 cnonce="0a4f113b",
1181 response="6629fae49393a05397450978507c4ef1",
1182 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1183*/
1184 var template (value) CommaParam_List digestCln := {
1185 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1186 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1187 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1188 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1189 };
1190 var template (value) WwwAuthenticate www_authenticate :=
1191 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1192
1193 var Authorization authorization :=
1194 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1195 "Mufasa",
1196 "Circle Of Life",
1197 "GET",
1198 "/dir/index.html",
1199 cnonce := "0a4f113b",
1200 nc_int := 1);
1201
1202 var CommaParam_List digestResp := authorization.body.digestResponse;
1203 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1204 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1205 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1206 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1207 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1208 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1209 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1210 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1211}
1212
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001213/* RFC 3261 8.1.1.5:
1214 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1215 * and MUST be less than 2**31."
1216 */
1217function f_sip_rand_seq_nr() return integer {
1218 /* 2**31 = 2147483648 */
1219 return f_rnd_int(2147483648)
1220}
Harald Welteb0d93602018-03-20 18:09:34 +01001221
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001222function f_sip_next_seq_nr(integer seq_nr) return integer {
1223 return (seq_nr + 1) mod 2147483648;
1224}
1225
1226function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1227 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1228}
1229
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001230function f_sip_rand_tag() return charstring {
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001231 /* Tags shall have at least 32 bit of randomness */
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001232 var integer rnd_int := f_rnd_int(4294967296);
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001233 /* Make collisions harder by appending time to the final string: */
1234 var integer ts_int := f_time_ms() mod 4294967296;
1235 return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8));
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001236}
1237
1238/* Generate a "branch" tag value.
1239 * RFC 3261 p.105 section 8:
1240 * "A common way to create this value is to compute a
1241 * cryptographic hash of the To tag, From tag, Call-ID header
1242 * field, the Request-URI of the request received (before
1243 * translation), the topmost Via header, and the sequence number
1244 * from the CSeq header field, in addition to any Proxy-Require
1245 * and Proxy-Authorization header fields that may be present. The
1246 * algorithm used to compute the hash is implementation-dependent,
1247 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1248 * choice."
1249 * See also Section 8.1.1.7:
1250 * "The branch ID inserted by an element compliant with this
1251 * specification MUST always begin with the characters "z9hG4bK"."
1252 */
1253const charstring sip_magic_cookie := "z9hG4bK";
1254function f_sip_gen_branch(charstring tag_to,
1255 charstring tag_from,
1256 charstring tag_call_id,
1257 integer cseq) return charstring {
1258 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1259 var charstring hash := f_calculateMD5(str);
1260 var charstring branch := sip_magic_cookie & hash;
1261 return branch;
1262}
1263
1264function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1265 var charstring str := "";
1266 if (ispresent(host_port.host)) {
1267 str := host_port.host;
1268 }
1269 if (ispresent(host_port.portField)) {
1270 str := str & ":" & int2str(host_port.portField);
1271 }
1272 return str;
1273}
1274
1275function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1276 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1277 return str;
1278}
1279
1280function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1281 if (ispresent(naddr.displayName)) {
1282 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1283 } else {
1284 return f_sip_SipUrl_to_str(naddr.addrSpec);
1285 }
1286}
1287
1288function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1289 if (ischosen(sip_addr.addr.nameAddr)) {
1290 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1291 } else {
1292 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1293 }
1294}
1295
Harald Welteb0d93602018-03-20 18:09:34 +01001296}