blob: 2498f49c505ea4ecb558e6eacfc5ed5280555162 [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,
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200457 template (omit) Expires expires := omit,
458 template (omit) WwwAuthenticate wwwAuthenticate := omit
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100459 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100460 allow := {
461 fieldName := ALLOW_E,
462 methods := allow_methods
463 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200464 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100465 callId := {
466 fieldName := CALL_ID_E,
467 callid := call_id
468 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100469 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100470 contentType := content_type,
471 cSeq := {
472 fieldName := CSEQ_E,
473 seqNumber := seq_nr,
474 method := method
475 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100476 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100477 fromField := {
478 fieldName := FROM_E,
479 addressField := from_addr.addr,
480 fromParams := from_addr.params
481 },
482 toField := {
483 fieldName := TO_E,
484 addressField := to_addr.addr,
485 toParams := to_addr.params
486 },
487 userAgent := {
488 fieldName := USER_AGENT_E,
489 userAgentBody := {
490 "osmo-ttcn3-hacks/0.23"
491 }
492 },
Pau Espin Pedrol89d8c952024-05-10 20:28:24 +0200493 via := via,
494 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100495}
496
Harald Welteb0d93602018-03-20 18:09:34 +0100497function tr_AllowMethods(template Method_List allow_methods) return template Allow {
498 if (istemplatekind(allow_methods, "omit")) {
499 return omit;
500 } else if (istemplatekind(allow_methods, "*")) {
501 return *;
502 } else if (istemplatekind(allow_methods, "?")) {
503 return ?;
504 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100505 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100506 fieldName := ALLOW_E,
507 methods := allow_methods
508 }
509 return ret
510}
511
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100512template (present) MessageHeader
513tr_SIP_msgh_std(template CallidString call_id,
514 template SipAddr from_addr,
515 template SipAddr to_addr,
516 template Contact contact,
517 template (present) Via via := tr_Via_from(?),
518 template charstring method,
519 template ContentType content_type := *,
520 template integer seq_nr := ?,
521 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200522 template Expires expires := *,
523 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100524 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100525 allow := tr_AllowMethods(allow_methods),
526 callId := {
527 fieldName := CALL_ID_E,
528 callid := call_id
529 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100530 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100531 contentType := content_type,
532 cSeq := {
533 fieldName := CSEQ_E,
534 seqNumber := seq_nr,
535 method := method
536 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100537 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100538 fromField := {
539 fieldName := FROM_E,
540 addressField := from_addr.addr,
541 fromParams := from_addr.params
542 },
543 toField := {
544 fieldName := TO_E,
545 addressField := to_addr.addr,
546 toParams := to_addr.params
547 },
548 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200549 via := via,
550 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100551}
552
553
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100554template (value) PDU_SIP_Request
555ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
556 template (value) CallidString call_id,
557 template (value) SipAddr from_addr,
558 template (value) SipAddr to_addr,
559 template (value) Via via,
560 integer seq_nr,
561 template (omit) Contact contact,
562 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200563 template (omit) Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100564 template (omit) charstring body := omit) := {
565 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
566 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
567 "REGISTER", seq_nr, via,
568 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200569 authorization := authorization,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100570 expires := expires),
571 messageBody := body,
572 payload := omit
573}
574template (present) PDU_SIP_Request
575tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
576 template (present) CallidString call_id := ?,
577 template (present) SipAddr from_addr := ?,
578 template (present) SipAddr to_addr := ?,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200579 template (present) Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100580 template integer seq_nr := *,
581 template Contact contact := *,
582 template Expires expires := *,
583 template charstring body := *) := {
584 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
585 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol0dd3f262024-04-25 17:04:43 +0200586 via, "REGISTER", *, seq_nr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100587 expires := expires),
588 messageBody := body,
589 payload := omit
590}
591
592template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200593ts_SIP_INVITE(template (value) CallidString call_id,
594 template (value) SipAddr from_addr,
595 template (value) SipAddr to_addr,
596 template (value) Via via,
597 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100598 integer seq_nr,
599 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100600 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200601 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100602 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200603 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100604 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100605 messageBody := body,
606 payload := omit
607}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100608template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200609tr_SIP_INVITE(template (present) SipUrl uri,
610 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100611 template SipAddr from_addr,
612 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200613 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100614 template integer seq_nr,
615 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200616 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100617 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200618 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100619 messageBody := body,
620 payload := omit
621}
622
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100623template (value) PDU_SIP_Request
624ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200625 template (value) SipAddr from_addr,
626 template (value) SipAddr to_addr,
627 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100628 integer seq_nr,
629 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100630 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
631 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200632 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100633 messageBody := body,
634 payload := omit
635}
636
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100637template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200638tr_SIP_BYE(template (present) SipUrl uri,
639 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100640 template SipAddr from_addr,
641 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200642 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100643 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200644 template charstring body := *) := {
645 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100646 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200647 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100648 messageBody := body,
649 payload := omit
650}
651
652
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100653template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200654ts_SIP_ACK(template (value) CallidString call_id,
655 template (value) SipAddr from_addr,
656 template (value) SipAddr to_addr,
657 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100658 integer seq_nr,
659 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100660 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100661 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
662 ts_Contact_SipAddr(from_addr),
663 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200664 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100665 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100666 messageBody := body,
667 payload := omit
668}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100669template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200670tr_SIP_ACK(template (present) SipUrl uri,
671 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100672 template SipAddr from_addr,
673 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200674 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100675 template integer seq_nr,
676 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200677 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100678 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200679 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100680 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100681 messageBody := body,
682 payload := omit
683}
684
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200685template (present) PDU_SIP_Request
686tr_SIP_CANCEL(template (present) SipUrl uri,
687 template (present) CallidString call_id,
688 template (present) SipAddr from_addr,
689 template (present) SipAddr to_addr,
690 template (present) Via via,
691 template (present) integer seq_nr,
692 template charstring body := *) := {
693 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
694 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
695 via,
696 "CANCEL", *, seq_nr),
697 messageBody := body,
698 payload := omit
699}
700
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100701template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200702ts_SIP_Response(template (value) CallidString call_id,
703 template (value) SipAddr from_addr,
704 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100705 charstring method,
706 integer status_code,
707 integer seq_nr,
708 charstring reason,
709 Via via,
710 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100711 statusLine := ts_SIP_StatusLine(status_code, reason),
712 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
713 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
714 messageBody := body,
715 payload := omit
716}
717
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200718/* 180 Ringing */
719template (value) PDU_SIP_Response
720ts_SIP_Response_Ringing(
721 template (value) CallidString call_id,
722 template (value) SipAddr from_addr,
723 template (value) SipAddr to_addr,
724 Via via,
725 integer seq_nr,
726 charstring method := "INVITE",
727 template (omit) charstring body := omit) := {
728 statusLine := ts_SIP_StatusLine(180, "Ringing"),
729 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
730 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
731 messageBody := body,
732 payload := omit
733}
734
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100735template (present) PDU_SIP_Response
736tr_SIP_Response(template CallidString call_id,
737 template SipAddr from_addr,
738 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200739 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100740 template Contact contact,
741 template charstring method,
742 template integer status_code,
743 template integer seq_nr := ?,
744 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200745 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100746 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100747 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200748 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100749 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100750 messageBody := body,
751 payload := omit
752}
753
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200754/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100755template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200756tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100757 template CallidString call_id,
758 template SipAddr from_addr,
759 template SipAddr to_addr,
760 template (present) Via via := tr_Via_from(?),
761 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200762 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100763 template integer seq_nr := ?,
764 template charstring method := "REGISTER",
765 template integer status_code := 401,
766 template charstring reason := "Unauthorized",
767 template charstring body := *) := {
768 statusLine := tr_SIP_StatusLine(status_code, reason),
769 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
770 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200771 method, *, seq_nr,
772 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100773 messageBody := body,
774 payload := omit
775}
Harald Welteb0d93602018-03-20 18:09:34 +0100776
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200777/* 100 Trying */
778template (present) PDU_SIP_Response
779tr_SIP_Response_Trying(
780 template CallidString call_id,
781 template SipAddr from_addr,
782 template SipAddr to_addr,
783 template (present) Via via := tr_Via_from(?),
784 template integer seq_nr := ?,
785 template charstring method := "INVITE",
786 template integer status_code := 100,
787 template charstring reason := "Trying",
788 template charstring body := *) := {
789 statusLine := tr_SIP_StatusLine(status_code, reason),
790 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
791 via,
792 method, *, seq_nr),
793 messageBody := body,
794 payload := omit
795}
796
797/* 180 Ringing */
798template (present) PDU_SIP_Response
799tr_SIP_Response_Ringing(
800 template CallidString call_id,
801 template SipAddr from_addr,
802 template SipAddr to_addr,
803 template (present) Via via := tr_Via_from(?),
804 template integer seq_nr := ?,
805 template charstring method := "INVITE",
806 template integer status_code := 180,
807 template charstring reason := "Ringing",
808 template charstring body := *) := {
809 statusLine := tr_SIP_StatusLine(status_code, reason),
810 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
811 via,
812 method, *, seq_nr),
813 messageBody := body,
814 payload := omit
815}
816
817/****************
818 * FUNCTIONS:
819 ****************/
820
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200821function f_sip_param_find(GenericParam_List li,
822 template (present) charstring id := ?)
823return template (omit) GenericParam {
824 var integer i;
825
826 for (i := 0; i < lengthof(li); i := i + 1) {
827 if (not ispresent(li[i])) {
828 continue;
829 }
830 if (match(li[i].id, id)) {
831 return li[i];
832 }
833 }
834 return omit;
835}
836
837function f_sip_param_find_or_fail(GenericParam_List li,
838 template (present) charstring id := ?)
839return GenericParam {
840 var template (omit) GenericParam parameter;
841 parameter := f_sip_param_find(li, id);
842 if (istemplatekind(parameter, "omit")) {
843 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
844 log2str("Param ", id, " not found in ", li));
845 }
846 return valueof(parameter);
847}
848
849function f_sip_param_get_value(GenericParam_List li,
850 template (present) charstring id := ?)
851return template (omit) charstring {
852 var template (omit) GenericParam parameter;
853 parameter := f_sip_param_find(li, id);
854 if (istemplatekind(parameter, "omit")) {
855 return omit;
856 }
857 return parameter.paramValue;
858}
859
860function f_sip_param_get_value_or_fail(GenericParam_List li,
861 template (present) charstring id := ?)
862return template (omit) charstring {
863 var GenericParam parameter;
864 parameter := f_sip_param_find_or_fail(li, id);
865 return parameter.paramValue;
866}
867
868function f_sip_param_get_value_present_or_fail(GenericParam_List li,
869 template (present) charstring id := ?)
870return charstring {
871 var GenericParam parameter;
872 parameter := f_sip_param_find_or_fail(li, id);
873 if (not ispresent(parameter.paramValue)) {
874 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
875 log2str("Param ", id, " value not present in ", li));
876 }
877 return parameter.paramValue;
878}
879
880function f_sip_param_match_value(GenericParam_List li,
881 template (present) charstring id := ?,
882 template charstring exp_paramValue := *)
883return boolean {
884 var template (omit) charstring val;
885 val := f_sip_param_get_value_or_fail(li, id);
886 if (istemplatekind(val, "omit")) {
887 return istemplatekind(val, "omit") or istemplatekind(val, "*");
888 }
889 return match(valueof(val), exp_paramValue);
890}
891
892function f_sip_param_match_value_or_fail(GenericParam_List li,
893 template (present) charstring id := ?,
894 template charstring exp_paramValue := *)
895{
896 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
897 if (istemplatekind(val, "omit")) {
898 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
899 return;
900 } else {
901 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
902 log2str("Param ", id, " match failed: val ", val,
903 " vs exp ", exp_paramValue));
904 }
905 }
906 if (not match(valueof(val), exp_paramValue)) {
907 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
908 log2str("Param ", id, " match failed: val ", val,
909 " vs exp ", exp_paramValue));
910 }
911}
912
913function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
914return GenericParam_List {
915 var integer i;
916 var GenericParam_List li;
917 var GenericParam_List new_li := {};
918
919 if (istemplatekind(li_tpl, "omit")) {
920 return {};
921 }
922
923 li := valueof(li_tpl);
924 for (i := 0; i < lengthof(li); i := i + 1) {
925 if (not ispresent(li[i]) or
926 not match(li[i].id, id)) {
927 new_li := new_li & {li[i]};
928 }
929 }
930 return new_li;
931}
932
933function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
934return GenericParam_List {
935 var integer i;
936 var GenericParam_List li;
937 var GenericParam_List new_li := {};
938 var boolean found := false;
939
940 if (istemplatekind(li_tpl, "omit")) {
941 return { valueof(ts_Param(id, val)) };
942 }
943
944 li := valueof(li_tpl);
945 for (i := 0; i < lengthof(li); i := i + 1) {
946 if (not ispresent(li[i]) or
947 not match(li[i].id, id)) {
948 new_li := new_li & {li[i]};
949 continue;
950 }
951 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
952 found := true;
953 }
954
955 if (not found) {
956 new_li := new_li & { valueof(ts_Param(id, val)) };
957 }
958 return new_li;
959}
960
961/* Make sure string is quoted. */
962function f_sip_str_quote(template (value) charstring val) return charstring {
963 var charstring str := valueof(val);
964 if (lengthof(str) == 0) {
965 return "";
966 }
967
968 if (str[0] != "\"") {
969 return "\"" & str & "\"";
970 }
971 return str;
972}
973
974/* Make sure string is unquoted.
975 * Similar to unq() in RFC 2617 */
976function f_sip_str_unquote(template (value) charstring val) return charstring {
977 var charstring str := valueof(val);
978 var integer len := lengthof(str);
979
980 if (len <= 1) {
981 return str;
982 }
983
984 if (str[0] == "\"" and str[len - 1] == "\"") {
985 return substr(str, 1, len - 2);
986 }
987 return str;
988}
989
990/* RFC 2617 3.2.2.2 A1 */
991function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
992
993 /* RFC 2617 3.2.2.2 A1 */
994 var charstring A1 := f_sip_str_unquote(user) & ":" &
995 f_sip_str_unquote(realm) & ":" &
996 password;
997 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
998 log("A1: md5('", A1, "') = ", digestA1);
999 return digestA1;
1000}
1001
1002/* RFC 2617 3.2.2.2 A2 */
1003function f_sip_digest_A2(charstring method, charstring uri) return charstring {
1004
1005 var charstring A2 := method & ":" & uri
1006 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1007 log("A2: md5('", A2, "') = ", digestA2);
1008 return digestA2;
1009}
1010
1011/* RFC 2617 3.2.2.1 Request-Digest */
1012function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1013 charstring nc, charstring cnonce,
1014 charstring qop, charstring digestA2) return charstring {
1015 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1016 nc & ":" &
1017 cnonce & ":" &
1018 f_sip_str_unquote(qop) & ":" &
1019 digestA2;
1020 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1021 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1022 return req_digest;
1023}
1024
1025/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1026 * KD(secret, data) = H(concat(secret, ":", data))
1027 */
1028function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1029 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1030}
1031
1032/* Digest Auth: RFC 2617 */
1033function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1034 charstring user, charstring password,
1035 charstring method, charstring uri,
1036 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1037 var CommaParam_List digestCln;
1038 var template (value) Authorization authorization;
1039 var template (value) Credentials cred;
1040 var template (omit) GenericParam rx_param;
1041
1042 digestCln := www_authenticate.challenge[0].digestCln;
1043
1044 var charstring algorithm;
1045 rx_param := f_sip_param_find(digestCln, "algorithm");
1046 if (istemplatekind(rx_param, "omit")) {
1047 /* Assume MD5 if not set */
1048 algorithm := "MD5"
1049 } else {
1050 algorithm := valueof(rx_param.paramValue);
1051 if (f_strstr(algorithm, "MD5") == -1) {
1052 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1053 log2str("Unexpected algorithm: ", algorithm));
1054 }
1055 }
1056
1057 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1058 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1059 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1060 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1061
1062 if (f_strstr(qop, "auth") == -1) {
1063 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1064 }
1065 var charstring selected_qop := "auth";
1066
1067 /* RFC 2617 3.2.2.2 A1 */
1068 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1069 /* RFC 2617 3.2.2.3 A2 */
1070 var charstring digestA2 := f_sip_digest_A2(method, uri);
1071
1072 /* RFC 2617 3.2.2.1 Request-Digest */
1073 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1074 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1075 nc, cnonce,
1076 selected_qop, digestA2);
1077
1078 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1079 uri, req_digest,
1080 opaque, algorithm, selected_qop, cnonce, nc);
1081
1082 authorization := ts_Authorization(cred);
1083 return valueof(authorization);
1084}
1085
1086/* RFC 2617 3.5 Example */
1087function f_sip_digest_selftest() {
1088/*
1089The following example assumes that an access-protected document is
1090being requested from the server via a GET request. The URI of the
1091document is "http://www.nowhere.org/dir/index.html". Both client and
1092server know that the username for this document is "Mufasa", and the
1093password is "Circle Of Life" (with one space between each of the
1094three words).
1095
1096HTTP/1.1 401 Unauthorized
1097WWW-Authenticate: Digest
1098 realm="testrealm@host.com",
1099 qop="auth,auth-int",
1100 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1101 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1102
1103Authorization: Digest username="Mufasa",
1104 realm="testrealm@host.com",
1105 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1106 uri="/dir/index.html",
1107 qop=auth,
1108 nc=00000001,
1109 cnonce="0a4f113b",
1110 response="6629fae49393a05397450978507c4ef1",
1111 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1112*/
1113 var template (value) CommaParam_List digestCln := {
1114 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1115 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1116 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1117 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1118 };
1119 var template (value) WwwAuthenticate www_authenticate :=
1120 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1121
1122 var Authorization authorization :=
1123 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1124 "Mufasa",
1125 "Circle Of Life",
1126 "GET",
1127 "/dir/index.html",
1128 cnonce := "0a4f113b",
1129 nc_int := 1);
1130
1131 var CommaParam_List digestResp := authorization.body.digestResponse;
1132 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1133 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1134 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1135 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1136 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1137 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1138 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1139 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1140}
1141
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001142/* RFC 3261 8.1.1.5:
1143 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1144 * and MUST be less than 2**31."
1145 */
1146function f_sip_rand_seq_nr() return integer {
1147 /* 2**31 = 2147483648 */
1148 return f_rnd_int(2147483648)
1149}
Harald Welteb0d93602018-03-20 18:09:34 +01001150
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001151function f_sip_next_seq_nr(integer seq_nr) return integer {
1152 return (seq_nr + 1) mod 2147483648;
1153}
1154
1155function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1156 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1157}
1158
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001159function f_sip_rand_tag() return charstring {
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001160 /* Tags shall have at least 32 bit of randomness */
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001161 var integer rnd_int := f_rnd_int(4294967296);
Pau Espin Pedrol95ad6a02024-04-18 16:52:46 +02001162 /* Make collisions harder by appending time to the final string: */
1163 var integer ts_int := f_time_ms() mod 4294967296;
1164 return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8));
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001165}
1166
1167/* Generate a "branch" tag value.
1168 * RFC 3261 p.105 section 8:
1169 * "A common way to create this value is to compute a
1170 * cryptographic hash of the To tag, From tag, Call-ID header
1171 * field, the Request-URI of the request received (before
1172 * translation), the topmost Via header, and the sequence number
1173 * from the CSeq header field, in addition to any Proxy-Require
1174 * and Proxy-Authorization header fields that may be present. The
1175 * algorithm used to compute the hash is implementation-dependent,
1176 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1177 * choice."
1178 * See also Section 8.1.1.7:
1179 * "The branch ID inserted by an element compliant with this
1180 * specification MUST always begin with the characters "z9hG4bK"."
1181 */
1182const charstring sip_magic_cookie := "z9hG4bK";
1183function f_sip_gen_branch(charstring tag_to,
1184 charstring tag_from,
1185 charstring tag_call_id,
1186 integer cseq) return charstring {
1187 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1188 var charstring hash := f_calculateMD5(str);
1189 var charstring branch := sip_magic_cookie & hash;
1190 return branch;
1191}
1192
1193function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1194 var charstring str := "";
1195 if (ispresent(host_port.host)) {
1196 str := host_port.host;
1197 }
1198 if (ispresent(host_port.portField)) {
1199 str := str & ":" & int2str(host_port.portField);
1200 }
1201 return str;
1202}
1203
1204function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1205 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1206 return str;
1207}
1208
1209function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1210 if (ispresent(naddr.displayName)) {
1211 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1212 } else {
1213 return f_sip_SipUrl_to_str(naddr.addrSpec);
1214 }
1215}
1216
1217function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1218 if (ischosen(sip_addr.addr.nameAddr)) {
1219 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1220 } else {
1221 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1222 }
1223}
1224
Harald Welteb0d93602018-03-20 18:09:34 +01001225}