blob: e22646cc42d598418c6c1a0ffdd2cda24b457e4d [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
200template (value) SipAddr ts_SipAddr(template (value) HostPort host_port,
201 template (omit) UserInfo user_info := omit,
202 template (omit) charstring displayName := omit,
203 template (omit) SemicolonParam_List params := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100204 addr := {
205 nameAddr := {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100206 displayName := displayName,
207 addrSpec := ts_SipUrl(host_port, user_info)
Harald Welteb0d93602018-03-20 18:09:34 +0100208 }
209 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100210 params := params
211}
212template (present) SipAddr tr_SipAddr(template (present) HostPort host_port := ?,
213 template UserInfo user_info := *,
214 template charstring displayName := *,
215 template SemicolonParam_List params := *) := {
216 addr := {
217 nameAddr := {
218 displayName := displayName,
219 addrSpec := tr_SipUrl(host_port, user_info)
220 }
221 },
222 params := params
Harald Welteb0d93602018-03-20 18:09:34 +0100223}
224
225/* build a receive template from a value: substitute '*' for omit */
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200226function tr_SipUrl_from_val(template (value) SipUrl tin) return template (present) SipUrl {
227 var template (present) SipUrl ret := tin;
228
229 /* if the port number is 5060, it may be omitted */
230 if (ispresent(tin.hostPort.portField) and
231 valueof(tin.hostPort.portField) == 5060) {
232 ret.hostPort.portField := 5060 ifpresent;
233 }
234 if (not ispresent(tin.userInfo.password)) {
235 ret.userInfo.password := *;
236 }
237
238 return ret;
239}
240function tr_SipAddr_from_val(template (value) SipAddr tin) return template (present) SipAddr {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100241 var template (present) SipAddr ret := tin;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200242
243 if (not ispresent(tin.addr.nameAddr.displayName)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100244 ret.addr.nameAddr.displayName := *;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200245 } else if (f_str_tolower(f_sip_str_unquote(tin.addr.nameAddr.displayName)) == "anonymous") {
246 /* if the user is Anonymous, it may be omitted */
247 ret.addr.nameAddr.displayName := tin.addr.nameAddr.displayName ifpresent;
Harald Welteb0d93602018-03-20 18:09:34 +0100248 }
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200249
250 ret.addr.nameAddr.addrSpec := tr_SipUrl_from_val(tin.addr.nameAddr.addrSpec);
251
252 if (not ispresent(tin.params)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100253 ret.params := *;
254 }
255 return ret;
256}
257
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200258function ts_SipAddr_from_Addr_Union(template (value) Addr_Union au,
259 template (omit) SemicolonParam_List params := omit)
260return template (value) SipAddr {
261 var template (value) SipUrl addrSpec := ts_SipUrl_from_Addr_Union(au);
262 var template (omit) charstring displayName;
263
264 if (ischosen(au.nameAddr)) {
265 displayName := au.nameAddr.displayName;
266 } else { /* au.addrSpecUnion */
267 displayName := omit
268 }
269
270 return ts_SipAddr(addrSpec.hostPort,
271 addrSpec.userInfo,
272 displayName,
273 params);
274}
275
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100276template (value) HostPort ts_HostPort(template (omit) charstring host := omit,
277 template (omit) integer portField := omit) := {
278 host := host,
279 portField := portField
280}
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200281
282template (present) HostPort tr_HostPort(template charstring host := *,
283 template integer portField := *) := {
284 host := host,
285 portField := portField
286}
287function f_tr_HostPort(template charstring host := *,
288 template integer portField := *)
289return template (present) HostPort {
290 return f_tr_HostPort_opt_defport(tr_HostPort(host, portField));
291}
292function f_tr_HostPort_opt_defport(template (present) HostPort hp) return template (present) HostPort {
293 var template (present) HostPort hpout := hp;
Harald Welteb0d93602018-03-20 18:09:34 +0100294 /* if the port number is 5060, it may be omitted */
295 if (isvalue(hp.portField) and valueof(hp.portField) == 5060) {
296 hpout.portField := 5060 ifpresent;
297 }
298 return hpout;
299}
300
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200301function f_tr_SipUrl_opt_defport(template (present) SipUrl url) return template (present) SipUrl {
302 var template (present) SipUrl urlout := url;
303 urlout.hostPort := f_tr_HostPort_opt_defport(url.hostPort);
304 return urlout;
305}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100306
307template (value) UserInfo ts_UserInfo(template (value) charstring userOrTelephoneSubscriber,
308 template (omit) charstring password := omit) := {
309 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
310 password := password
311}
312template (present) UserInfo tr_UserInfo(template (present) charstring userOrTelephoneSubscriber := ?,
313 template charstring password := *) := {
314 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
315 password := password
316}
317
318template (value) RequestLine ts_SIP_ReqLine(Method method,
319 template (value) SipUrl uri,
Harald Welteb0d93602018-03-20 18:09:34 +0100320 charstring ver := c_SIP_VERSION) := {
321 method := method,
322 requestUri := uri,
323 sipVersion := ver
324}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100325template (present) RequestLine tr_SIP_ReqLine(template (present) Method method := ?,
326 template (present) SipUrl uri := ?,
327 template (present) charstring ver := c_SIP_VERSION) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100328 method := method,
329 requestUri := uri,
330 sipVersion := ver
331}
332
333template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := {
334 sipVersion := "SIP/2.0",
335 statusCode := status_code,
336 reasonPhrase := reason
337}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100338template (present) StatusLine tr_SIP_StatusLine(template integer status_code,
339 template charstring reason) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100340 sipVersion := "SIP/2.0",
341 statusCode := status_code,
342 reasonPhrase := reason
343}
344
345
346template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := {
347 requestLine := rl,
348 msgHeader := c_SIP_msgHeader_empty,
349 messageBody := omit,
350 payload := omit
351}
352
353const Method_List c_SIP_defaultMethods := {
354 "INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE",
355 "NOTIFY", "REFER", "UPDATE" };
356
357private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body)
358return template (omit) ContentType {
359 /* if user explicitly stated no content type */
360 if (istemplatekind(ct, "omit")) {
361 return omit;
362 }
363 /* if there's no body, then there's no content-type either */
364 if (istemplatekind(body, "omit")) {
365 return omit;
366 }
367 return ct;
368}
369
370template (value) ContentType ts_CT_SDP := {
371 fieldName := CONTENT_TYPE_E,
372 mediaType := "application/sdp"
373};
374
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200375template (value) Via ts_Via_from(template (value) HostPort addr,
376 template (value) charstring transport := "UDP") := {
Harald Welteb0d93602018-03-20 18:09:34 +0100377 fieldName := VIA_E,
378 viaBody := {
379 {
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200380 sentProtocol := { "SIP", "2.0", transport },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100381 sentBy := addr,
Harald Welteb0d93602018-03-20 18:09:34 +0100382 viaParams := omit
383 }
384 }
385}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100386template (present) Via tr_Via_from(template (present) HostPort host_port := ?,
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200387 template (present) charstring transport := ?,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100388 template SemicolonParam_List viaParams := *) := {
389 fieldName := VIA_E,
390 viaBody := {
391 {
Pau Espin Pedrol2a833372024-05-10 20:23:22 +0200392 sentProtocol := { "SIP", "2.0", ? },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100393 sentBy := host_port,
394 viaParams := viaParams
395 }
396 }
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200397}
398
399template (present) OtherAuth
400tr_OtherAuth(template (present) charstring authScheme := ?,
401 template (present) CommaParam_List authParams := ?) := {
402 authScheme := authScheme,
403 authParams := authParams
404}
405
406template (value) OtherAuth
407ts_OtherAuth(template (value) charstring authScheme,
408 template (value) CommaParam_List authParams) := {
409 authScheme := authScheme,
410 authParams := authParams
411}
412
413template (present) Challenge
414tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := {
415 digestCln := digestCln
416}
417
418template (value) Challenge
419ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := {
420 digestCln := digestCln
421}
422
423template (present) Challenge
424tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := {
425 otherChallenge := otherChallenge
426}
427
428template (value) Challenge
429ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := {
430 otherChallenge := otherChallenge
431}
432
433template (present) WwwAuthenticate
434tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := {
435 fieldName := WWW_AUTHENTICATE_E,
436 challenge := challenge
437}
438
439template (value) WwwAuthenticate
440ts_WwwAuthenticate(template (value) Challenge_list challenge) := {
441 fieldName := WWW_AUTHENTICATE_E,
442 challenge := challenge
443}
Harald Welteb0d93602018-03-20 18:09:34 +0100444
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100445template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
446template (value) MessageHeader
447ts_SIP_msgh_std(template (value) CallidString call_id,
448 template (value) SipAddr from_addr,
449 template (value) SipAddr to_addr,
450 template (omit) Contact contact,
451 template (value) charstring method,
452 template (value) integer seq_nr,
453 template (value) Via via,
454 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200455 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100456 template (value) Method_List allow_methods := c_SIP_defaultMethods,
457 template (omit) Expires expires := omit
458 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100459 allow := {
460 fieldName := ALLOW_E,
461 methods := allow_methods
462 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200463 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100464 callId := {
465 fieldName := CALL_ID_E,
466 callid := call_id
467 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100468 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100469 contentType := content_type,
470 cSeq := {
471 fieldName := CSEQ_E,
472 seqNumber := seq_nr,
473 method := method
474 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100475 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100476 fromField := {
477 fieldName := FROM_E,
478 addressField := from_addr.addr,
479 fromParams := from_addr.params
480 },
481 toField := {
482 fieldName := TO_E,
483 addressField := to_addr.addr,
484 toParams := to_addr.params
485 },
486 userAgent := {
487 fieldName := USER_AGENT_E,
488 userAgentBody := {
489 "osmo-ttcn3-hacks/0.23"
490 }
491 },
492 via := via
493}
494
Harald Welteb0d93602018-03-20 18:09:34 +0100495function tr_AllowMethods(template Method_List allow_methods) return template Allow {
496 if (istemplatekind(allow_methods, "omit")) {
497 return omit;
498 } else if (istemplatekind(allow_methods, "*")) {
499 return *;
500 } else if (istemplatekind(allow_methods, "?")) {
501 return ?;
502 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100503 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100504 fieldName := ALLOW_E,
505 methods := allow_methods
506 }
507 return ret
508}
509
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100510template (present) MessageHeader
511tr_SIP_msgh_std(template CallidString call_id,
512 template SipAddr from_addr,
513 template SipAddr to_addr,
514 template Contact contact,
515 template (present) Via via := tr_Via_from(?),
516 template charstring method,
517 template ContentType content_type := *,
518 template integer seq_nr := ?,
519 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200520 template Expires expires := *,
521 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100522 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100523 allow := tr_AllowMethods(allow_methods),
524 callId := {
525 fieldName := CALL_ID_E,
526 callid := call_id
527 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100528 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100529 contentType := content_type,
530 cSeq := {
531 fieldName := CSEQ_E,
532 seqNumber := seq_nr,
533 method := method
534 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100535 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100536 fromField := {
537 fieldName := FROM_E,
538 addressField := from_addr.addr,
539 fromParams := from_addr.params
540 },
541 toField := {
542 fieldName := TO_E,
543 addressField := to_addr.addr,
544 toParams := to_addr.params
545 },
546 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200547 via := via,
548 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100549}
550
551
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100552template (value) PDU_SIP_Request
553ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
554 template (value) CallidString call_id,
555 template (value) SipAddr from_addr,
556 template (value) SipAddr to_addr,
557 template (value) Via via,
558 integer seq_nr,
559 template (omit) Contact contact,
560 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200561 template (omit) Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100562 template (omit) charstring body := omit) := {
563 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
564 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
565 "REGISTER", seq_nr, via,
566 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200567 authorization := authorization,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100568 expires := expires),
569 messageBody := body,
570 payload := omit
571}
572template (present) PDU_SIP_Request
573tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
574 template (present) CallidString call_id := ?,
575 template (present) SipAddr from_addr := ?,
576 template (present) SipAddr to_addr := ?,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200577 template (present) Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100578 template integer seq_nr := *,
579 template Contact contact := *,
580 template Expires expires := *,
581 template charstring body := *) := {
582 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
583 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200584 via, "REGISTER", *, seq_nr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100585 expires := expires),
586 messageBody := body,
587 payload := omit
588}
589
590template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200591ts_SIP_INVITE(template (value) CallidString call_id,
592 template (value) SipAddr from_addr,
593 template (value) SipAddr to_addr,
594 template (value) Via via,
595 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100596 integer seq_nr,
597 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100598 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200599 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100600 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200601 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100602 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100603 messageBody := body,
604 payload := omit
605}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100606template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200607tr_SIP_INVITE(template (present) SipUrl uri,
608 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100609 template SipAddr from_addr,
610 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200611 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100612 template integer seq_nr,
613 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200614 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100615 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200616 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100617 messageBody := body,
618 payload := omit
619}
620
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100621template (value) PDU_SIP_Request
622ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200623 template (value) SipAddr from_addr,
624 template (value) SipAddr to_addr,
625 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100626 integer seq_nr,
627 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100628 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
629 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200630 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100631 messageBody := body,
632 payload := omit
633}
634
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100635template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200636tr_SIP_BYE(template (present) SipUrl uri,
637 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100638 template SipAddr from_addr,
639 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200640 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100641 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200642 template charstring body := *) := {
643 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100644 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200645 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100646 messageBody := body,
647 payload := omit
648}
649
650
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100651template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200652ts_SIP_ACK(template (value) CallidString call_id,
653 template (value) SipAddr from_addr,
654 template (value) SipAddr to_addr,
655 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100656 integer seq_nr,
657 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100658 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100659 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
660 ts_Contact_SipAddr(from_addr),
661 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200662 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100663 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100664 messageBody := body,
665 payload := omit
666}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100667template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200668tr_SIP_ACK(template (present) SipUrl uri,
669 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100670 template SipAddr from_addr,
671 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200672 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100673 template integer seq_nr,
674 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200675 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100676 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200677 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100678 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100679 messageBody := body,
680 payload := omit
681}
682
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200683template (present) PDU_SIP_Request
684tr_SIP_CANCEL(template (present) SipUrl uri,
685 template (present) CallidString call_id,
686 template (present) SipAddr from_addr,
687 template (present) SipAddr to_addr,
688 template (present) Via via,
689 template (present) integer seq_nr,
690 template charstring body := *) := {
691 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
692 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
693 via,
694 "CANCEL", *, seq_nr),
695 messageBody := body,
696 payload := omit
697}
698
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100699template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200700ts_SIP_Response(template (value) CallidString call_id,
701 template (value) SipAddr from_addr,
702 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100703 charstring method,
704 integer status_code,
705 integer seq_nr,
706 charstring reason,
707 Via via,
708 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100709 statusLine := ts_SIP_StatusLine(status_code, reason),
710 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
711 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
712 messageBody := body,
713 payload := omit
714}
715
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200716/* 180 Ringing */
717template (value) PDU_SIP_Response
718ts_SIP_Response_Ringing(
719 template (value) CallidString call_id,
720 template (value) SipAddr from_addr,
721 template (value) SipAddr to_addr,
722 Via via,
723 integer seq_nr,
724 charstring method := "INVITE",
725 template (omit) charstring body := omit) := {
726 statusLine := ts_SIP_StatusLine(180, "Ringing"),
727 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
728 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
729 messageBody := body,
730 payload := omit
731}
732
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100733template (present) PDU_SIP_Response
734tr_SIP_Response(template CallidString call_id,
735 template SipAddr from_addr,
736 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200737 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100738 template Contact contact,
739 template charstring method,
740 template integer status_code,
741 template integer seq_nr := ?,
742 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200743 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100744 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100745 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200746 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100747 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100748 messageBody := body,
749 payload := omit
750}
751
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200752/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100753template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200754tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100755 template CallidString call_id,
756 template SipAddr from_addr,
757 template SipAddr to_addr,
758 template (present) Via via := tr_Via_from(?),
759 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200760 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100761 template integer seq_nr := ?,
762 template charstring method := "REGISTER",
763 template integer status_code := 401,
764 template charstring reason := "Unauthorized",
765 template charstring body := *) := {
766 statusLine := tr_SIP_StatusLine(status_code, reason),
767 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
768 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200769 method, *, seq_nr,
770 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100771 messageBody := body,
772 payload := omit
773}
Harald Welteb0d93602018-03-20 18:09:34 +0100774
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200775/* 100 Trying */
776template (present) PDU_SIP_Response
777tr_SIP_Response_Trying(
778 template CallidString call_id,
779 template SipAddr from_addr,
780 template SipAddr to_addr,
781 template (present) Via via := tr_Via_from(?),
782 template integer seq_nr := ?,
783 template charstring method := "INVITE",
784 template integer status_code := 100,
785 template charstring reason := "Trying",
786 template charstring body := *) := {
787 statusLine := tr_SIP_StatusLine(status_code, reason),
788 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
789 via,
790 method, *, seq_nr),
791 messageBody := body,
792 payload := omit
793}
794
795/* 180 Ringing */
796template (present) PDU_SIP_Response
797tr_SIP_Response_Ringing(
798 template CallidString call_id,
799 template SipAddr from_addr,
800 template SipAddr to_addr,
801 template (present) Via via := tr_Via_from(?),
802 template integer seq_nr := ?,
803 template charstring method := "INVITE",
804 template integer status_code := 180,
805 template charstring reason := "Ringing",
806 template charstring body := *) := {
807 statusLine := tr_SIP_StatusLine(status_code, reason),
808 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
809 via,
810 method, *, seq_nr),
811 messageBody := body,
812 payload := omit
813}
814
815/****************
816 * FUNCTIONS:
817 ****************/
818
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200819function f_sip_param_find(GenericParam_List li,
820 template (present) charstring id := ?)
821return template (omit) GenericParam {
822 var integer i;
823
824 for (i := 0; i < lengthof(li); i := i + 1) {
825 if (not ispresent(li[i])) {
826 continue;
827 }
828 if (match(li[i].id, id)) {
829 return li[i];
830 }
831 }
832 return omit;
833}
834
835function f_sip_param_find_or_fail(GenericParam_List li,
836 template (present) charstring id := ?)
837return GenericParam {
838 var template (omit) GenericParam parameter;
839 parameter := f_sip_param_find(li, id);
840 if (istemplatekind(parameter, "omit")) {
841 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
842 log2str("Param ", id, " not found in ", li));
843 }
844 return valueof(parameter);
845}
846
847function f_sip_param_get_value(GenericParam_List li,
848 template (present) charstring id := ?)
849return template (omit) charstring {
850 var template (omit) GenericParam parameter;
851 parameter := f_sip_param_find(li, id);
852 if (istemplatekind(parameter, "omit")) {
853 return omit;
854 }
855 return parameter.paramValue;
856}
857
858function f_sip_param_get_value_or_fail(GenericParam_List li,
859 template (present) charstring id := ?)
860return template (omit) charstring {
861 var GenericParam parameter;
862 parameter := f_sip_param_find_or_fail(li, id);
863 return parameter.paramValue;
864}
865
866function f_sip_param_get_value_present_or_fail(GenericParam_List li,
867 template (present) charstring id := ?)
868return charstring {
869 var GenericParam parameter;
870 parameter := f_sip_param_find_or_fail(li, id);
871 if (not ispresent(parameter.paramValue)) {
872 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
873 log2str("Param ", id, " value not present in ", li));
874 }
875 return parameter.paramValue;
876}
877
878function f_sip_param_match_value(GenericParam_List li,
879 template (present) charstring id := ?,
880 template charstring exp_paramValue := *)
881return boolean {
882 var template (omit) charstring val;
883 val := f_sip_param_get_value_or_fail(li, id);
884 if (istemplatekind(val, "omit")) {
885 return istemplatekind(val, "omit") or istemplatekind(val, "*");
886 }
887 return match(valueof(val), exp_paramValue);
888}
889
890function f_sip_param_match_value_or_fail(GenericParam_List li,
891 template (present) charstring id := ?,
892 template charstring exp_paramValue := *)
893{
894 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
895 if (istemplatekind(val, "omit")) {
896 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
897 return;
898 } else {
899 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
900 log2str("Param ", id, " match failed: val ", val,
901 " vs exp ", exp_paramValue));
902 }
903 }
904 if (not match(valueof(val), exp_paramValue)) {
905 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
906 log2str("Param ", id, " match failed: val ", val,
907 " vs exp ", exp_paramValue));
908 }
909}
910
911function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
912return GenericParam_List {
913 var integer i;
914 var GenericParam_List li;
915 var GenericParam_List new_li := {};
916
917 if (istemplatekind(li_tpl, "omit")) {
918 return {};
919 }
920
921 li := valueof(li_tpl);
922 for (i := 0; i < lengthof(li); i := i + 1) {
923 if (not ispresent(li[i]) or
924 not match(li[i].id, id)) {
925 new_li := new_li & {li[i]};
926 }
927 }
928 return new_li;
929}
930
931function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
932return GenericParam_List {
933 var integer i;
934 var GenericParam_List li;
935 var GenericParam_List new_li := {};
936 var boolean found := false;
937
938 if (istemplatekind(li_tpl, "omit")) {
939 return { valueof(ts_Param(id, val)) };
940 }
941
942 li := valueof(li_tpl);
943 for (i := 0; i < lengthof(li); i := i + 1) {
944 if (not ispresent(li[i]) or
945 not match(li[i].id, id)) {
946 new_li := new_li & {li[i]};
947 continue;
948 }
949 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
950 found := true;
951 }
952
953 if (not found) {
954 new_li := new_li & { valueof(ts_Param(id, val)) };
955 }
956 return new_li;
957}
958
959/* Make sure string is quoted. */
960function f_sip_str_quote(template (value) charstring val) return charstring {
961 var charstring str := valueof(val);
962 if (lengthof(str) == 0) {
963 return "";
964 }
965
966 if (str[0] != "\"") {
967 return "\"" & str & "\"";
968 }
969 return str;
970}
971
972/* Make sure string is unquoted.
973 * Similar to unq() in RFC 2617 */
974function f_sip_str_unquote(template (value) charstring val) return charstring {
975 var charstring str := valueof(val);
976 var integer len := lengthof(str);
977
978 if (len <= 1) {
979 return str;
980 }
981
982 if (str[0] == "\"" and str[len - 1] == "\"") {
983 return substr(str, 1, len - 2);
984 }
985 return str;
986}
987
988/* RFC 2617 3.2.2.2 A1 */
989function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
990
991 /* RFC 2617 3.2.2.2 A1 */
992 var charstring A1 := f_sip_str_unquote(user) & ":" &
993 f_sip_str_unquote(realm) & ":" &
994 password;
995 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
996 log("A1: md5('", A1, "') = ", digestA1);
997 return digestA1;
998}
999
1000/* RFC 2617 3.2.2.2 A2 */
1001function f_sip_digest_A2(charstring method, charstring uri) return charstring {
1002
1003 var charstring A2 := method & ":" & uri
1004 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1005 log("A2: md5('", A2, "') = ", digestA2);
1006 return digestA2;
1007}
1008
1009/* RFC 2617 3.2.2.1 Request-Digest */
1010function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1011 charstring nc, charstring cnonce,
1012 charstring qop, charstring digestA2) return charstring {
1013 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1014 nc & ":" &
1015 cnonce & ":" &
1016 f_sip_str_unquote(qop) & ":" &
1017 digestA2;
1018 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1019 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1020 return req_digest;
1021}
1022
1023/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1024 * KD(secret, data) = H(concat(secret, ":", data))
1025 */
1026function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1027 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1028}
1029
1030/* Digest Auth: RFC 2617 */
1031function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1032 charstring user, charstring password,
1033 charstring method, charstring uri,
1034 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1035 var CommaParam_List digestCln;
1036 var template (value) Authorization authorization;
1037 var template (value) Credentials cred;
1038 var template (omit) GenericParam rx_param;
1039
1040 digestCln := www_authenticate.challenge[0].digestCln;
1041
1042 var charstring algorithm;
1043 rx_param := f_sip_param_find(digestCln, "algorithm");
1044 if (istemplatekind(rx_param, "omit")) {
1045 /* Assume MD5 if not set */
1046 algorithm := "MD5"
1047 } else {
1048 algorithm := valueof(rx_param.paramValue);
1049 if (f_strstr(algorithm, "MD5") == -1) {
1050 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1051 log2str("Unexpected algorithm: ", algorithm));
1052 }
1053 }
1054
1055 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1056 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1057 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1058 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1059
1060 if (f_strstr(qop, "auth") == -1) {
1061 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1062 }
1063 var charstring selected_qop := "auth";
1064
1065 /* RFC 2617 3.2.2.2 A1 */
1066 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1067 /* RFC 2617 3.2.2.3 A2 */
1068 var charstring digestA2 := f_sip_digest_A2(method, uri);
1069
1070 /* RFC 2617 3.2.2.1 Request-Digest */
1071 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1072 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1073 nc, cnonce,
1074 selected_qop, digestA2);
1075
1076 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1077 uri, req_digest,
1078 opaque, algorithm, selected_qop, cnonce, nc);
1079
1080 authorization := ts_Authorization(cred);
1081 return valueof(authorization);
1082}
1083
1084/* RFC 2617 3.5 Example */
1085function f_sip_digest_selftest() {
1086/*
1087The following example assumes that an access-protected document is
1088being requested from the server via a GET request. The URI of the
1089document is "http://www.nowhere.org/dir/index.html". Both client and
1090server know that the username for this document is "Mufasa", and the
1091password is "Circle Of Life" (with one space between each of the
1092three words).
1093
1094HTTP/1.1 401 Unauthorized
1095WWW-Authenticate: Digest
1096 realm="testrealm@host.com",
1097 qop="auth,auth-int",
1098 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1099 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1100
1101Authorization: Digest username="Mufasa",
1102 realm="testrealm@host.com",
1103 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1104 uri="/dir/index.html",
1105 qop=auth,
1106 nc=00000001,
1107 cnonce="0a4f113b",
1108 response="6629fae49393a05397450978507c4ef1",
1109 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1110*/
1111 var template (value) CommaParam_List digestCln := {
1112 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1113 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1114 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1115 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1116 };
1117 var template (value) WwwAuthenticate www_authenticate :=
1118 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1119
1120 var Authorization authorization :=
1121 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1122 "Mufasa",
1123 "Circle Of Life",
1124 "GET",
1125 "/dir/index.html",
1126 cnonce := "0a4f113b",
1127 nc_int := 1);
1128
1129 var CommaParam_List digestResp := authorization.body.digestResponse;
1130 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1131 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1132 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1133 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1134 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1135 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1136 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1137 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1138}
1139
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001140/* RFC 3261 8.1.1.5:
1141 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1142 * and MUST be less than 2**31."
1143 */
1144function f_sip_rand_seq_nr() return integer {
1145 /* 2**31 = 2147483648 */
1146 return f_rnd_int(2147483648)
1147}
Harald Welteb0d93602018-03-20 18:09:34 +01001148
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001149function f_sip_next_seq_nr(integer seq_nr) return integer {
1150 return (seq_nr + 1) mod 2147483648;
1151}
1152
1153function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1154 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1155}
1156
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001157function f_sip_rand_tag() return charstring {
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001158 /* Tags shall have at least 32 bit of randomness */
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001159 var integer rnd_int := f_rnd_int(4294967296);
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001160 /* Make collisions harder by appending time to the final string: */
1161 var integer ts_int := f_time_ms() mod 4294967296;
1162 return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8));
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001163}
1164
1165/* Generate a "branch" tag value.
1166 * RFC 3261 p.105 section 8:
1167 * "A common way to create this value is to compute a
1168 * cryptographic hash of the To tag, From tag, Call-ID header
1169 * field, the Request-URI of the request received (before
1170 * translation), the topmost Via header, and the sequence number
1171 * from the CSeq header field, in addition to any Proxy-Require
1172 * and Proxy-Authorization header fields that may be present. The
1173 * algorithm used to compute the hash is implementation-dependent,
1174 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1175 * choice."
1176 * See also Section 8.1.1.7:
1177 * "The branch ID inserted by an element compliant with this
1178 * specification MUST always begin with the characters "z9hG4bK"."
1179 */
1180const charstring sip_magic_cookie := "z9hG4bK";
1181function f_sip_gen_branch(charstring tag_to,
1182 charstring tag_from,
1183 charstring tag_call_id,
1184 integer cseq) return charstring {
1185 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1186 var charstring hash := f_calculateMD5(str);
1187 var charstring branch := sip_magic_cookie & hash;
1188 return branch;
1189}
1190
1191function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1192 var charstring str := "";
1193 if (ispresent(host_port.host)) {
1194 str := host_port.host;
1195 }
1196 if (ispresent(host_port.portField)) {
1197 str := str & ":" & int2str(host_port.portField);
1198 }
1199 return str;
1200}
1201
1202function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1203 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1204 return str;
1205}
1206
1207function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1208 if (ispresent(naddr.displayName)) {
1209 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1210 } else {
1211 return f_sip_SipUrl_to_str(naddr.addrSpec);
1212 }
1213}
1214
1215function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1216 if (ischosen(sip_addr.addr.nameAddr)) {
1217 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1218 } else {
1219 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1220 }
1221}
1222
Harald Welteb0d93602018-03-20 18:09:34 +01001223}