blob: e115bb7caa9b0de30e059f29a65cf9747e3328cd [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 Pedrolfb34d862024-03-28 20:21:38 +0100375template (value) Via ts_Via_from(template (value) HostPort addr) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100376 fieldName := VIA_E,
377 viaBody := {
378 {
379 sentProtocol := { "SIP", "2.0", "UDP" },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100380 sentBy := addr,
Harald Welteb0d93602018-03-20 18:09:34 +0100381 viaParams := omit
382 }
383 }
384}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100385template (present) Via tr_Via_from(template (present) HostPort host_port := ?,
386 template SemicolonParam_List viaParams := *) := {
387 fieldName := VIA_E,
388 viaBody := {
389 {
390 sentProtocol := { "SIP", "2.0", "UDP" },
391 sentBy := host_port,
392 viaParams := viaParams
393 }
394 }
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200395}
396
397template (present) OtherAuth
398tr_OtherAuth(template (present) charstring authScheme := ?,
399 template (present) CommaParam_List authParams := ?) := {
400 authScheme := authScheme,
401 authParams := authParams
402}
403
404template (value) OtherAuth
405ts_OtherAuth(template (value) charstring authScheme,
406 template (value) CommaParam_List authParams) := {
407 authScheme := authScheme,
408 authParams := authParams
409}
410
411template (present) Challenge
412tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := {
413 digestCln := digestCln
414}
415
416template (value) Challenge
417ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := {
418 digestCln := digestCln
419}
420
421template (present) Challenge
422tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := {
423 otherChallenge := otherChallenge
424}
425
426template (value) Challenge
427ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := {
428 otherChallenge := otherChallenge
429}
430
431template (present) WwwAuthenticate
432tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := {
433 fieldName := WWW_AUTHENTICATE_E,
434 challenge := challenge
435}
436
437template (value) WwwAuthenticate
438ts_WwwAuthenticate(template (value) Challenge_list challenge) := {
439 fieldName := WWW_AUTHENTICATE_E,
440 challenge := challenge
441}
Harald Welteb0d93602018-03-20 18:09:34 +0100442
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100443template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
444template (value) MessageHeader
445ts_SIP_msgh_std(template (value) CallidString call_id,
446 template (value) SipAddr from_addr,
447 template (value) SipAddr to_addr,
448 template (omit) Contact contact,
449 template (value) charstring method,
450 template (value) integer seq_nr,
451 template (value) Via via,
452 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200453 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100454 template (value) Method_List allow_methods := c_SIP_defaultMethods,
455 template (omit) Expires expires := omit
456 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100457 allow := {
458 fieldName := ALLOW_E,
459 methods := allow_methods
460 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200461 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100462 callId := {
463 fieldName := CALL_ID_E,
464 callid := call_id
465 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100466 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100467 contentType := content_type,
468 cSeq := {
469 fieldName := CSEQ_E,
470 seqNumber := seq_nr,
471 method := method
472 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100473 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100474 fromField := {
475 fieldName := FROM_E,
476 addressField := from_addr.addr,
477 fromParams := from_addr.params
478 },
479 toField := {
480 fieldName := TO_E,
481 addressField := to_addr.addr,
482 toParams := to_addr.params
483 },
484 userAgent := {
485 fieldName := USER_AGENT_E,
486 userAgentBody := {
487 "osmo-ttcn3-hacks/0.23"
488 }
489 },
490 via := via
491}
492
Harald Welteb0d93602018-03-20 18:09:34 +0100493function tr_AllowMethods(template Method_List allow_methods) return template Allow {
494 if (istemplatekind(allow_methods, "omit")) {
495 return omit;
496 } else if (istemplatekind(allow_methods, "*")) {
497 return *;
498 } else if (istemplatekind(allow_methods, "?")) {
499 return ?;
500 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100501 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100502 fieldName := ALLOW_E,
503 methods := allow_methods
504 }
505 return ret
506}
507
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100508template (present) MessageHeader
509tr_SIP_msgh_std(template CallidString call_id,
510 template SipAddr from_addr,
511 template SipAddr to_addr,
512 template Contact contact,
513 template (present) Via via := tr_Via_from(?),
514 template charstring method,
515 template ContentType content_type := *,
516 template integer seq_nr := ?,
517 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200518 template Expires expires := *,
519 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100520 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100521 allow := tr_AllowMethods(allow_methods),
522 callId := {
523 fieldName := CALL_ID_E,
524 callid := call_id
525 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100526 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100527 contentType := content_type,
528 cSeq := {
529 fieldName := CSEQ_E,
530 seqNumber := seq_nr,
531 method := method
532 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100533 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100534 fromField := {
535 fieldName := FROM_E,
536 addressField := from_addr.addr,
537 fromParams := from_addr.params
538 },
539 toField := {
540 fieldName := TO_E,
541 addressField := to_addr.addr,
542 toParams := to_addr.params
543 },
544 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200545 via := via,
546 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100547}
548
549
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100550template (value) PDU_SIP_Request
551ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
552 template (value) CallidString call_id,
553 template (value) SipAddr from_addr,
554 template (value) SipAddr to_addr,
555 template (value) Via via,
556 integer seq_nr,
557 template (omit) Contact contact,
558 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200559 template (omit) Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100560 template (omit) charstring body := omit) := {
561 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
562 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
563 "REGISTER", seq_nr, via,
564 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200565 authorization := authorization,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100566 expires := expires),
567 messageBody := body,
568 payload := omit
569}
570template (present) PDU_SIP_Request
571tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
572 template (present) CallidString call_id := ?,
573 template (present) SipAddr from_addr := ?,
574 template (present) SipAddr to_addr := ?,
575 template integer seq_nr := *,
576 template Contact contact := *,
577 template Expires expires := *,
578 template charstring body := *) := {
579 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
580 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200581 tr_Via_from(f_tr_HostPort_opt_defport(from_addr.addr.nameAddr.addrSpec.hostPort)),
Pau Espin Pedrol65dde832024-04-08 18:15:58 +0200582 "REGISTER", *, seq_nr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100583 expires := expires),
584 messageBody := body,
585 payload := omit
586}
587
588template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200589ts_SIP_INVITE(template (value) CallidString call_id,
590 template (value) SipAddr from_addr,
591 template (value) SipAddr to_addr,
592 template (value) Via via,
593 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100594 integer seq_nr,
595 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100596 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200597 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100598 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200599 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100600 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100601 messageBody := body,
602 payload := omit
603}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100604template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200605tr_SIP_INVITE(template (present) SipUrl uri,
606 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100607 template SipAddr from_addr,
608 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200609 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100610 template integer seq_nr,
611 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200612 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100613 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200614 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100615 messageBody := body,
616 payload := omit
617}
618
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100619template (value) PDU_SIP_Request
620ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200621 template (value) SipAddr from_addr,
622 template (value) SipAddr to_addr,
623 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100624 integer seq_nr,
625 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100626 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
627 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200628 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100629 messageBody := body,
630 payload := omit
631}
632
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100633template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200634tr_SIP_BYE(template (present) SipUrl uri,
635 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100636 template SipAddr from_addr,
637 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200638 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100639 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200640 template charstring body := *) := {
641 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100642 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200643 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100644 messageBody := body,
645 payload := omit
646}
647
648
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100649template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200650ts_SIP_ACK(template (value) CallidString call_id,
651 template (value) SipAddr from_addr,
652 template (value) SipAddr to_addr,
653 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100654 integer seq_nr,
655 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100656 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100657 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
658 ts_Contact_SipAddr(from_addr),
659 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200660 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100661 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100662 messageBody := body,
663 payload := omit
664}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100665template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200666tr_SIP_ACK(template (present) SipUrl uri,
667 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100668 template SipAddr from_addr,
669 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200670 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100671 template integer seq_nr,
672 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200673 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100674 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200675 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100676 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100677 messageBody := body,
678 payload := omit
679}
680
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200681template (present) PDU_SIP_Request
682tr_SIP_CANCEL(template (present) SipUrl uri,
683 template (present) CallidString call_id,
684 template (present) SipAddr from_addr,
685 template (present) SipAddr to_addr,
686 template (present) Via via,
687 template (present) integer seq_nr,
688 template charstring body := *) := {
689 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
690 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
691 via,
692 "CANCEL", *, seq_nr),
693 messageBody := body,
694 payload := omit
695}
696
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100697template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200698ts_SIP_Response(template (value) CallidString call_id,
699 template (value) SipAddr from_addr,
700 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100701 charstring method,
702 integer status_code,
703 integer seq_nr,
704 charstring reason,
705 Via via,
706 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100707 statusLine := ts_SIP_StatusLine(status_code, reason),
708 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
709 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
710 messageBody := body,
711 payload := omit
712}
713
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200714/* 180 Ringing */
715template (value) PDU_SIP_Response
716ts_SIP_Response_Ringing(
717 template (value) CallidString call_id,
718 template (value) SipAddr from_addr,
719 template (value) SipAddr to_addr,
720 Via via,
721 integer seq_nr,
722 charstring method := "INVITE",
723 template (omit) charstring body := omit) := {
724 statusLine := ts_SIP_StatusLine(180, "Ringing"),
725 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
726 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
727 messageBody := body,
728 payload := omit
729}
730
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100731template (present) PDU_SIP_Response
732tr_SIP_Response(template CallidString call_id,
733 template SipAddr from_addr,
734 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200735 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100736 template Contact contact,
737 template charstring method,
738 template integer status_code,
739 template integer seq_nr := ?,
740 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200741 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100742 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100743 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200744 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100745 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100746 messageBody := body,
747 payload := omit
748}
749
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200750/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100751template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200752tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100753 template CallidString call_id,
754 template SipAddr from_addr,
755 template SipAddr to_addr,
756 template (present) Via via := tr_Via_from(?),
757 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200758 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100759 template integer seq_nr := ?,
760 template charstring method := "REGISTER",
761 template integer status_code := 401,
762 template charstring reason := "Unauthorized",
763 template charstring body := *) := {
764 statusLine := tr_SIP_StatusLine(status_code, reason),
765 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
766 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200767 method, *, seq_nr,
768 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100769 messageBody := body,
770 payload := omit
771}
Harald Welteb0d93602018-03-20 18:09:34 +0100772
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200773/* 100 Trying */
774template (present) PDU_SIP_Response
775tr_SIP_Response_Trying(
776 template CallidString call_id,
777 template SipAddr from_addr,
778 template SipAddr to_addr,
779 template (present) Via via := tr_Via_from(?),
780 template integer seq_nr := ?,
781 template charstring method := "INVITE",
782 template integer status_code := 100,
783 template charstring reason := "Trying",
784 template charstring body := *) := {
785 statusLine := tr_SIP_StatusLine(status_code, reason),
786 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
787 via,
788 method, *, seq_nr),
789 messageBody := body,
790 payload := omit
791}
792
793/* 180 Ringing */
794template (present) PDU_SIP_Response
795tr_SIP_Response_Ringing(
796 template CallidString call_id,
797 template SipAddr from_addr,
798 template SipAddr to_addr,
799 template (present) Via via := tr_Via_from(?),
800 template integer seq_nr := ?,
801 template charstring method := "INVITE",
802 template integer status_code := 180,
803 template charstring reason := "Ringing",
804 template charstring body := *) := {
805 statusLine := tr_SIP_StatusLine(status_code, reason),
806 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
807 via,
808 method, *, seq_nr),
809 messageBody := body,
810 payload := omit
811}
812
813/****************
814 * FUNCTIONS:
815 ****************/
816
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200817function f_sip_param_find(GenericParam_List li,
818 template (present) charstring id := ?)
819return template (omit) GenericParam {
820 var integer i;
821
822 for (i := 0; i < lengthof(li); i := i + 1) {
823 if (not ispresent(li[i])) {
824 continue;
825 }
826 if (match(li[i].id, id)) {
827 return li[i];
828 }
829 }
830 return omit;
831}
832
833function f_sip_param_find_or_fail(GenericParam_List li,
834 template (present) charstring id := ?)
835return GenericParam {
836 var template (omit) GenericParam parameter;
837 parameter := f_sip_param_find(li, id);
838 if (istemplatekind(parameter, "omit")) {
839 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
840 log2str("Param ", id, " not found in ", li));
841 }
842 return valueof(parameter);
843}
844
845function f_sip_param_get_value(GenericParam_List li,
846 template (present) charstring id := ?)
847return template (omit) charstring {
848 var template (omit) GenericParam parameter;
849 parameter := f_sip_param_find(li, id);
850 if (istemplatekind(parameter, "omit")) {
851 return omit;
852 }
853 return parameter.paramValue;
854}
855
856function f_sip_param_get_value_or_fail(GenericParam_List li,
857 template (present) charstring id := ?)
858return template (omit) charstring {
859 var GenericParam parameter;
860 parameter := f_sip_param_find_or_fail(li, id);
861 return parameter.paramValue;
862}
863
864function f_sip_param_get_value_present_or_fail(GenericParam_List li,
865 template (present) charstring id := ?)
866return charstring {
867 var GenericParam parameter;
868 parameter := f_sip_param_find_or_fail(li, id);
869 if (not ispresent(parameter.paramValue)) {
870 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
871 log2str("Param ", id, " value not present in ", li));
872 }
873 return parameter.paramValue;
874}
875
876function f_sip_param_match_value(GenericParam_List li,
877 template (present) charstring id := ?,
878 template charstring exp_paramValue := *)
879return boolean {
880 var template (omit) charstring val;
881 val := f_sip_param_get_value_or_fail(li, id);
882 if (istemplatekind(val, "omit")) {
883 return istemplatekind(val, "omit") or istemplatekind(val, "*");
884 }
885 return match(valueof(val), exp_paramValue);
886}
887
888function f_sip_param_match_value_or_fail(GenericParam_List li,
889 template (present) charstring id := ?,
890 template charstring exp_paramValue := *)
891{
892 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
893 if (istemplatekind(val, "omit")) {
894 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
895 return;
896 } else {
897 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
898 log2str("Param ", id, " match failed: val ", val,
899 " vs exp ", exp_paramValue));
900 }
901 }
902 if (not match(valueof(val), exp_paramValue)) {
903 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
904 log2str("Param ", id, " match failed: val ", val,
905 " vs exp ", exp_paramValue));
906 }
907}
908
909function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
910return GenericParam_List {
911 var integer i;
912 var GenericParam_List li;
913 var GenericParam_List new_li := {};
914
915 if (istemplatekind(li_tpl, "omit")) {
916 return {};
917 }
918
919 li := valueof(li_tpl);
920 for (i := 0; i < lengthof(li); i := i + 1) {
921 if (not ispresent(li[i]) or
922 not match(li[i].id, id)) {
923 new_li := new_li & {li[i]};
924 }
925 }
926 return new_li;
927}
928
929function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
930return GenericParam_List {
931 var integer i;
932 var GenericParam_List li;
933 var GenericParam_List new_li := {};
934 var boolean found := false;
935
936 if (istemplatekind(li_tpl, "omit")) {
937 return { valueof(ts_Param(id, val)) };
938 }
939
940 li := valueof(li_tpl);
941 for (i := 0; i < lengthof(li); i := i + 1) {
942 if (not ispresent(li[i]) or
943 not match(li[i].id, id)) {
944 new_li := new_li & {li[i]};
945 continue;
946 }
947 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
948 found := true;
949 }
950
951 if (not found) {
952 new_li := new_li & { valueof(ts_Param(id, val)) };
953 }
954 return new_li;
955}
956
957/* Make sure string is quoted. */
958function f_sip_str_quote(template (value) charstring val) return charstring {
959 var charstring str := valueof(val);
960 if (lengthof(str) == 0) {
961 return "";
962 }
963
964 if (str[0] != "\"") {
965 return "\"" & str & "\"";
966 }
967 return str;
968}
969
970/* Make sure string is unquoted.
971 * Similar to unq() in RFC 2617 */
972function f_sip_str_unquote(template (value) charstring val) return charstring {
973 var charstring str := valueof(val);
974 var integer len := lengthof(str);
975
976 if (len <= 1) {
977 return str;
978 }
979
980 if (str[0] == "\"" and str[len - 1] == "\"") {
981 return substr(str, 1, len - 2);
982 }
983 return str;
984}
985
986/* RFC 2617 3.2.2.2 A1 */
987function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
988
989 /* RFC 2617 3.2.2.2 A1 */
990 var charstring A1 := f_sip_str_unquote(user) & ":" &
991 f_sip_str_unquote(realm) & ":" &
992 password;
993 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
994 log("A1: md5('", A1, "') = ", digestA1);
995 return digestA1;
996}
997
998/* RFC 2617 3.2.2.2 A2 */
999function f_sip_digest_A2(charstring method, charstring uri) return charstring {
1000
1001 var charstring A2 := method & ":" & uri
1002 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1003 log("A2: md5('", A2, "') = ", digestA2);
1004 return digestA2;
1005}
1006
1007/* RFC 2617 3.2.2.1 Request-Digest */
1008function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1009 charstring nc, charstring cnonce,
1010 charstring qop, charstring digestA2) return charstring {
1011 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1012 nc & ":" &
1013 cnonce & ":" &
1014 f_sip_str_unquote(qop) & ":" &
1015 digestA2;
1016 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1017 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1018 return req_digest;
1019}
1020
1021/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1022 * KD(secret, data) = H(concat(secret, ":", data))
1023 */
1024function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1025 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1026}
1027
1028/* Digest Auth: RFC 2617 */
1029function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1030 charstring user, charstring password,
1031 charstring method, charstring uri,
1032 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1033 var CommaParam_List digestCln;
1034 var template (value) Authorization authorization;
1035 var template (value) Credentials cred;
1036 var template (omit) GenericParam rx_param;
1037
1038 digestCln := www_authenticate.challenge[0].digestCln;
1039
1040 var charstring algorithm;
1041 rx_param := f_sip_param_find(digestCln, "algorithm");
1042 if (istemplatekind(rx_param, "omit")) {
1043 /* Assume MD5 if not set */
1044 algorithm := "MD5"
1045 } else {
1046 algorithm := valueof(rx_param.paramValue);
1047 if (f_strstr(algorithm, "MD5") == -1) {
1048 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1049 log2str("Unexpected algorithm: ", algorithm));
1050 }
1051 }
1052
1053 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1054 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1055 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1056 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1057
1058 if (f_strstr(qop, "auth") == -1) {
1059 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1060 }
1061 var charstring selected_qop := "auth";
1062
1063 /* RFC 2617 3.2.2.2 A1 */
1064 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1065 /* RFC 2617 3.2.2.3 A2 */
1066 var charstring digestA2 := f_sip_digest_A2(method, uri);
1067
1068 /* RFC 2617 3.2.2.1 Request-Digest */
1069 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1070 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1071 nc, cnonce,
1072 selected_qop, digestA2);
1073
1074 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1075 uri, req_digest,
1076 opaque, algorithm, selected_qop, cnonce, nc);
1077
1078 authorization := ts_Authorization(cred);
1079 return valueof(authorization);
1080}
1081
1082/* RFC 2617 3.5 Example */
1083function f_sip_digest_selftest() {
1084/*
1085The following example assumes that an access-protected document is
1086being requested from the server via a GET request. The URI of the
1087document is "http://www.nowhere.org/dir/index.html". Both client and
1088server know that the username for this document is "Mufasa", and the
1089password is "Circle Of Life" (with one space between each of the
1090three words).
1091
1092HTTP/1.1 401 Unauthorized
1093WWW-Authenticate: Digest
1094 realm="testrealm@host.com",
1095 qop="auth,auth-int",
1096 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1097 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1098
1099Authorization: Digest username="Mufasa",
1100 realm="testrealm@host.com",
1101 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1102 uri="/dir/index.html",
1103 qop=auth,
1104 nc=00000001,
1105 cnonce="0a4f113b",
1106 response="6629fae49393a05397450978507c4ef1",
1107 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1108*/
1109 var template (value) CommaParam_List digestCln := {
1110 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1111 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1112 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1113 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1114 };
1115 var template (value) WwwAuthenticate www_authenticate :=
1116 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1117
1118 var Authorization authorization :=
1119 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1120 "Mufasa",
1121 "Circle Of Life",
1122 "GET",
1123 "/dir/index.html",
1124 cnonce := "0a4f113b",
1125 nc_int := 1);
1126
1127 var CommaParam_List digestResp := authorization.body.digestResponse;
1128 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1129 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1130 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1131 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1132 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1133 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1134 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1135 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1136}
1137
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001138/* RFC 3261 8.1.1.5:
1139 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1140 * and MUST be less than 2**31."
1141 */
1142function f_sip_rand_seq_nr() return integer {
1143 /* 2**31 = 2147483648 */
1144 return f_rnd_int(2147483648)
1145}
Harald Welteb0d93602018-03-20 18:09:34 +01001146
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001147function f_sip_next_seq_nr(integer seq_nr) return integer {
1148 return (seq_nr + 1) mod 2147483648;
1149}
1150
1151function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1152 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1153}
1154
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001155function f_sip_rand_tag() return charstring {
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001156 /* Tags shall have at least 32 bit of randomness */
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001157 var integer rnd_int := f_rnd_int(4294967296);
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001158 /* Make collisions harder by appending time to the final string: */
1159 var integer ts_int := f_time_ms() mod 4294967296;
1160 return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8));
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001161}
1162
1163/* Generate a "branch" tag value.
1164 * RFC 3261 p.105 section 8:
1165 * "A common way to create this value is to compute a
1166 * cryptographic hash of the To tag, From tag, Call-ID header
1167 * field, the Request-URI of the request received (before
1168 * translation), the topmost Via header, and the sequence number
1169 * from the CSeq header field, in addition to any Proxy-Require
1170 * and Proxy-Authorization header fields that may be present. The
1171 * algorithm used to compute the hash is implementation-dependent,
1172 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1173 * choice."
1174 * See also Section 8.1.1.7:
1175 * "The branch ID inserted by an element compliant with this
1176 * specification MUST always begin with the characters "z9hG4bK"."
1177 */
1178const charstring sip_magic_cookie := "z9hG4bK";
1179function f_sip_gen_branch(charstring tag_to,
1180 charstring tag_from,
1181 charstring tag_call_id,
1182 integer cseq) return charstring {
1183 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1184 var charstring hash := f_calculateMD5(str);
1185 var charstring branch := sip_magic_cookie & hash;
1186 return branch;
1187}
1188
1189function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1190 var charstring str := "";
1191 if (ispresent(host_port.host)) {
1192 str := host_port.host;
1193 }
1194 if (ispresent(host_port.portField)) {
1195 str := str & ":" & int2str(host_port.portField);
1196 }
1197 return str;
1198}
1199
1200function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1201 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1202 return str;
1203}
1204
1205function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1206 if (ispresent(naddr.displayName)) {
1207 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1208 } else {
1209 return f_sip_SipUrl_to_str(naddr.addrSpec);
1210 }
1211}
1212
1213function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1214 if (ischosen(sip_addr.addr.nameAddr)) {
1215 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1216 } else {
1217 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1218 }
1219}
1220
Harald Welteb0d93602018-03-20 18:09:34 +01001221}