blob: be07196e2d6bc8f18db239e1f9824d938352c6ec [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;
6import from Native_Functions all;
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01007import from Osmocom_Types all;
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02008import from Misc_Helpers all;
Harald Welteb0d93602018-03-20 18:09:34 +01009
10/* wrapper type to encapsulate the Addr_Union + parameter list used in From, To. ... */
11type record SipAddr {
12 Addr_Union addr,
13 SemicolonParam_List params optional
14}
15
16const charstring c_SIP_VERSION := "SIP/2.0";
17
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020018template (value) GenericParam ts_Param(template (value) charstring id,
19 template (omit) charstring paramValue := omit) := {
20 id := id,
21 paramValue := paramValue
22}
23template (present) GenericParam tr_Param(template (present) charstring id := ?,
24 template charstring paramValue := *) := {
25 id := id,
26 paramValue := paramValue
27}
28function f_ts_Param_omit(template (value) charstring id,
29 template (omit) charstring paramValue := omit)
30 return template (omit) GenericParam
31{
32 if (istemplatekind(paramValue, "omit")) {
33 return omit;
34 }
35 return ts_Param(id, paramValue);
36}
37
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010038template (value) SipUrl ts_SipUrl(template (value) HostPort host_port,
39 template (omit) UserInfo user_info := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +010040 scheme := "sip",
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010041 userInfo := user_info,
42 hostPort := host_port,
Harald Welteb0d93602018-03-20 18:09:34 +010043 urlParameters := omit,
44 headers := omit
45}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010046template (present) SipUrl tr_SipUrl(template (present) HostPort host_port := ?,
47 template UserInfo user_info := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +010048 scheme := "sip",
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010049 userInfo := user_info,
50 hostPort := host_port,
Harald Welteb0d93602018-03-20 18:09:34 +010051 urlParameters := *,
52 headers := *
53}
54
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +020055template (value) SipUrl ts_SipUrlHost(template (value) charstring host,
56 template (omit) integer portField := omit)
57 := ts_SipUrl(ts_HostPort(host, portField));
58
59function ts_SipUrl_from_Addr_Union(template (value) Addr_Union au)
60return template (value) SipUrl {
61 if (ischosen(au.nameAddr)) {
62 return au.nameAddr.addrSpec;
63 } else { /* au.addrSpecUnion */
64 return au.addrSpecUnion;
65 }
66}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +010067
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020068template (value) Credentials ts_Credentials_DigestResponse(template (value) CommaParam_List digestResponse) := {
69 digestResponse := digestResponse
70}
71
72template (value) Credentials ts_Credentials_DigestResponseMD5(
73 template (value) charstring username,
74 template (value) charstring realm,
75 template (value) charstring nonce,
76 template (value) charstring uri,
77 template (value) charstring response,
78 template (value) charstring opaque,
79 template (value) charstring algorithm := "MD5",
80 template (value) charstring qop := "auth",
81 template (omit) charstring cnonce := omit,
82 template (omit) charstring nc := omit
83 ) := {
84 digestResponse := {
85 // Already added by digestResponse automatically:
86 //ts_Param("Digest", omit),
87 ts_Param("username", f_sip_str_quote(username)),
88 ts_Param("realm", f_sip_str_quote(realm)),
89 ts_Param("nonce", f_sip_str_quote(nonce)),
90 ts_Param("uri", f_sip_str_quote(uri)),
91 ts_Param("response", f_sip_str_quote(response)),
92 ts_Param("opaque", f_sip_str_quote(opaque)),
93 ts_Param("algorithm", algorithm),
94 ts_Param("qop", qop),
95 // FIXME: If "omit" is passed, these below end up in;
96 // "Dynamic test case error: Performing a valueof or send operation on a non-specific template of type @SIPmsg_Types.GenericParam"
97 f_ts_Param_omit("cnonce", f_sip_str_quote(cnonce)),
98 f_ts_Param_omit("nc", nc)
99 }
100}
101
102template (value) Credentials ts_Credentials_OtherAuth(template (value) OtherAuth otherResponse) := {
103 otherResponse := otherResponse
104}
105
106template (value) Authorization ts_Authorization(template (value) Credentials body) := {
107 fieldName := AUTHORIZATION_E,
108 body := body
109}
110
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100111// [20.10]
112template (present) NameAddr tr_NameAddr(template (present) SipUrl addrSpec := ?,
113 template charstring displayName := *) := {
114 displayName := displayName,
115 addrSpec := addrSpec
Harald Welteb0d93602018-03-20 18:09:34 +0100116}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100117template (value) NameAddr ts_NameAddr(template (value) SipUrl addrSpec,
118 template (omit) charstring displayName := omit) := {
119 displayName := displayName,
120 addrSpec := addrSpec
121}
122
123template (present) Addr_Union tr_Addr_Union_NameAddr(template (present) NameAddr nameAddr := ?) := {
124 nameAddr := nameAddr
125}
126template (value) Addr_Union ts_Addr_Union_NameAddr(template (value) NameAddr nameAddr) := {
127 nameAddr := nameAddr
128}
129
130template (present) Addr_Union tr_Addr_Union_SipUrl(template (present) SipUrl sipUrl := ?) := {
131 addrSpecUnion := sipUrl
132}
133template (value) Addr_Union ts_Addr_Union_SipUrl(template (value) SipUrl sipUrl) := {
134 addrSpecUnion := sipUrl
135}
136
137
138template (present) ContactAddress tr_ContactAddress(template (present) Addr_Union addressField := ?,
139 template SemicolonParam_List contactParams := *) := {
140 addressField := addressField,
141 contactParams := contactParams
142}
143template (value) ContactAddress ts_ContactAddress(template (value) Addr_Union addressField,
144 template (omit) SemicolonParam_List contactParams := omit) := {
145 addressField := addressField,
146 contactParams := contactParams
147}
148
149template (present) Contact tr_Contact(template (present) ContactAddress_List contactAddresses := ?) := {
150 fieldName := CONTACT_E,
151 contactBody := {
152 contactAddresses := contactAddresses
153 }
154}
155template (value) Contact ts_Contact(template (value) ContactAddress_List contactAddresses) := {
156 fieldName := CONTACT_E,
157 contactBody := {
158 contactAddresses := contactAddresses
159 }
160}
161
162template (value) Contact ts_ContactWildcard := {
163 fieldName := CONTACT_E,
164 contactBody := {
165 wildcard := "*"
166 }
167}
168
169template (present) Contact tr_Contact_SipAddr(template (present) SipAddr contact_addr := ?)
170 := tr_Contact({ tr_ContactAddress(contact_addr.addr, contact_addr.params) });
171
172private function f_tr_Contact_SipAddr(template SipAddr contact_addr) return template Contact
173{
174 if (istemplatekind(contact_addr, "omit")) {
175 return omit;
176 } else if (istemplatekind(contact_addr, "*")) {
177 return *;
178 }
179 return tr_Contact_SipAddr(contact_addr);
180}
181
182template (value) Contact ts_Contact_SipAddr(template (value) SipAddr contact_addr)
183 := ts_Contact({ ts_ContactAddress(contact_addr.addr, contact_addr.params) });
184private function ts_Contact_SipAddr_omit(template (omit) SipAddr contact_addr := omit) return template (omit) Contact
185{
186 if (istemplatekind(contact_addr, "omit")) {
187 return omit;
188 }
189 return ts_Contact_SipAddr(contact_addr);
190}
191
192
193// [20.19]
194template (value) Expires ts_Expires(template (value) DeltaSec deltaSec := "7200") := {
195 fieldName := EXPIRES_E,
196 deltaSec := deltaSec
197}
198
199template (value) SipAddr ts_SipAddr(template (value) HostPort host_port,
200 template (omit) UserInfo user_info := omit,
201 template (omit) charstring displayName := omit,
202 template (omit) SemicolonParam_List params := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100203 addr := {
204 nameAddr := {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100205 displayName := displayName,
206 addrSpec := ts_SipUrl(host_port, user_info)
Harald Welteb0d93602018-03-20 18:09:34 +0100207 }
208 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100209 params := params
210}
211template (present) SipAddr tr_SipAddr(template (present) HostPort host_port := ?,
212 template UserInfo user_info := *,
213 template charstring displayName := *,
214 template SemicolonParam_List params := *) := {
215 addr := {
216 nameAddr := {
217 displayName := displayName,
218 addrSpec := tr_SipUrl(host_port, user_info)
219 }
220 },
221 params := params
Harald Welteb0d93602018-03-20 18:09:34 +0100222}
223
224/* build a receive template from a value: substitute '*' for omit */
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200225function tr_SipUrl_from_val(template (value) SipUrl tin) return template (present) SipUrl {
226 var template (present) SipUrl ret := tin;
227
228 /* if the port number is 5060, it may be omitted */
229 if (ispresent(tin.hostPort.portField) and
230 valueof(tin.hostPort.portField) == 5060) {
231 ret.hostPort.portField := 5060 ifpresent;
232 }
233 if (not ispresent(tin.userInfo.password)) {
234 ret.userInfo.password := *;
235 }
236
237 return ret;
238}
239function tr_SipAddr_from_val(template (value) SipAddr tin) return template (present) SipAddr {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100240 var template (present) SipAddr ret := tin;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200241
242 if (not ispresent(tin.addr.nameAddr.displayName)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100243 ret.addr.nameAddr.displayName := *;
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200244 } else if (f_str_tolower(f_sip_str_unquote(tin.addr.nameAddr.displayName)) == "anonymous") {
245 /* if the user is Anonymous, it may be omitted */
246 ret.addr.nameAddr.displayName := tin.addr.nameAddr.displayName ifpresent;
Harald Welteb0d93602018-03-20 18:09:34 +0100247 }
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200248
249 ret.addr.nameAddr.addrSpec := tr_SipUrl_from_val(tin.addr.nameAddr.addrSpec);
250
251 if (not ispresent(tin.params)) {
Harald Welteb0d93602018-03-20 18:09:34 +0100252 ret.params := *;
253 }
254 return ret;
255}
256
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200257function ts_SipAddr_from_Addr_Union(template (value) Addr_Union au,
258 template (omit) SemicolonParam_List params := omit)
259return template (value) SipAddr {
260 var template (value) SipUrl addrSpec := ts_SipUrl_from_Addr_Union(au);
261 var template (omit) charstring displayName;
262
263 if (ischosen(au.nameAddr)) {
264 displayName := au.nameAddr.displayName;
265 } else { /* au.addrSpecUnion */
266 displayName := omit
267 }
268
269 return ts_SipAddr(addrSpec.hostPort,
270 addrSpec.userInfo,
271 displayName,
272 params);
273}
274
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100275template (value) HostPort ts_HostPort(template (omit) charstring host := omit,
276 template (omit) integer portField := omit) := {
277 host := host,
278 portField := portField
279}
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200280
281template (present) HostPort tr_HostPort(template charstring host := *,
282 template integer portField := *) := {
283 host := host,
284 portField := portField
285}
286function f_tr_HostPort(template charstring host := *,
287 template integer portField := *)
288return template (present) HostPort {
289 return f_tr_HostPort_opt_defport(tr_HostPort(host, portField));
290}
291function f_tr_HostPort_opt_defport(template (present) HostPort hp) return template (present) HostPort {
292 var template (present) HostPort hpout := hp;
Harald Welteb0d93602018-03-20 18:09:34 +0100293 /* if the port number is 5060, it may be omitted */
294 if (isvalue(hp.portField) and valueof(hp.portField) == 5060) {
295 hpout.portField := 5060 ifpresent;
296 }
297 return hpout;
298}
299
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200300function f_tr_SipUrl_opt_defport(template (present) SipUrl url) return template (present) SipUrl {
301 var template (present) SipUrl urlout := url;
302 urlout.hostPort := f_tr_HostPort_opt_defport(url.hostPort);
303 return urlout;
304}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100305
306template (value) UserInfo ts_UserInfo(template (value) charstring userOrTelephoneSubscriber,
307 template (omit) charstring password := omit) := {
308 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
309 password := password
310}
311template (present) UserInfo tr_UserInfo(template (present) charstring userOrTelephoneSubscriber := ?,
312 template charstring password := *) := {
313 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
314 password := password
315}
316
317template (value) RequestLine ts_SIP_ReqLine(Method method,
318 template (value) SipUrl uri,
Harald Welteb0d93602018-03-20 18:09:34 +0100319 charstring ver := c_SIP_VERSION) := {
320 method := method,
321 requestUri := uri,
322 sipVersion := ver
323}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100324template (present) RequestLine tr_SIP_ReqLine(template (present) Method method := ?,
325 template (present) SipUrl uri := ?,
326 template (present) charstring ver := c_SIP_VERSION) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100327 method := method,
328 requestUri := uri,
329 sipVersion := ver
330}
331
332template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := {
333 sipVersion := "SIP/2.0",
334 statusCode := status_code,
335 reasonPhrase := reason
336}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100337template (present) StatusLine tr_SIP_StatusLine(template integer status_code,
338 template charstring reason) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100339 sipVersion := "SIP/2.0",
340 statusCode := status_code,
341 reasonPhrase := reason
342}
343
344
345template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := {
346 requestLine := rl,
347 msgHeader := c_SIP_msgHeader_empty,
348 messageBody := omit,
349 payload := omit
350}
351
352const Method_List c_SIP_defaultMethods := {
353 "INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE",
354 "NOTIFY", "REFER", "UPDATE" };
355
356private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body)
357return template (omit) ContentType {
358 /* if user explicitly stated no content type */
359 if (istemplatekind(ct, "omit")) {
360 return omit;
361 }
362 /* if there's no body, then there's no content-type either */
363 if (istemplatekind(body, "omit")) {
364 return omit;
365 }
366 return ct;
367}
368
369template (value) ContentType ts_CT_SDP := {
370 fieldName := CONTENT_TYPE_E,
371 mediaType := "application/sdp"
372};
373
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100374template (value) Via ts_Via_from(template (value) HostPort addr) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100375 fieldName := VIA_E,
376 viaBody := {
377 {
378 sentProtocol := { "SIP", "2.0", "UDP" },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100379 sentBy := addr,
Harald Welteb0d93602018-03-20 18:09:34 +0100380 viaParams := omit
381 }
382 }
383}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100384template (present) Via tr_Via_from(template (present) HostPort host_port := ?,
385 template SemicolonParam_List viaParams := *) := {
386 fieldName := VIA_E,
387 viaBody := {
388 {
389 sentProtocol := { "SIP", "2.0", "UDP" },
390 sentBy := host_port,
391 viaParams := viaParams
392 }
393 }
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200394}
395
396template (present) OtherAuth
397tr_OtherAuth(template (present) charstring authScheme := ?,
398 template (present) CommaParam_List authParams := ?) := {
399 authScheme := authScheme,
400 authParams := authParams
401}
402
403template (value) OtherAuth
404ts_OtherAuth(template (value) charstring authScheme,
405 template (value) CommaParam_List authParams) := {
406 authScheme := authScheme,
407 authParams := authParams
408}
409
410template (present) Challenge
411tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := {
412 digestCln := digestCln
413}
414
415template (value) Challenge
416ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := {
417 digestCln := digestCln
418}
419
420template (present) Challenge
421tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := {
422 otherChallenge := otherChallenge
423}
424
425template (value) Challenge
426ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := {
427 otherChallenge := otherChallenge
428}
429
430template (present) WwwAuthenticate
431tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := {
432 fieldName := WWW_AUTHENTICATE_E,
433 challenge := challenge
434}
435
436template (value) WwwAuthenticate
437ts_WwwAuthenticate(template (value) Challenge_list challenge) := {
438 fieldName := WWW_AUTHENTICATE_E,
439 challenge := challenge
440}
Harald Welteb0d93602018-03-20 18:09:34 +0100441
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100442template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
443template (value) MessageHeader
444ts_SIP_msgh_std(template (value) CallidString call_id,
445 template (value) SipAddr from_addr,
446 template (value) SipAddr to_addr,
447 template (omit) Contact contact,
448 template (value) charstring method,
449 template (value) integer seq_nr,
450 template (value) Via via,
451 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200452 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100453 template (value) Method_List allow_methods := c_SIP_defaultMethods,
454 template (omit) Expires expires := omit
455 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100456 allow := {
457 fieldName := ALLOW_E,
458 methods := allow_methods
459 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200460 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100461 callId := {
462 fieldName := CALL_ID_E,
463 callid := call_id
464 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100465 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100466 contentType := content_type,
467 cSeq := {
468 fieldName := CSEQ_E,
469 seqNumber := seq_nr,
470 method := method
471 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100472 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100473 fromField := {
474 fieldName := FROM_E,
475 addressField := from_addr.addr,
476 fromParams := from_addr.params
477 },
478 toField := {
479 fieldName := TO_E,
480 addressField := to_addr.addr,
481 toParams := to_addr.params
482 },
483 userAgent := {
484 fieldName := USER_AGENT_E,
485 userAgentBody := {
486 "osmo-ttcn3-hacks/0.23"
487 }
488 },
489 via := via
490}
491
Harald Welteb0d93602018-03-20 18:09:34 +0100492function tr_AllowMethods(template Method_List allow_methods) return template Allow {
493 if (istemplatekind(allow_methods, "omit")) {
494 return omit;
495 } else if (istemplatekind(allow_methods, "*")) {
496 return *;
497 } else if (istemplatekind(allow_methods, "?")) {
498 return ?;
499 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100500 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100501 fieldName := ALLOW_E,
502 methods := allow_methods
503 }
504 return ret
505}
506
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100507template (present) MessageHeader
508tr_SIP_msgh_std(template CallidString call_id,
509 template SipAddr from_addr,
510 template SipAddr to_addr,
511 template Contact contact,
512 template (present) Via via := tr_Via_from(?),
513 template charstring method,
514 template ContentType content_type := *,
515 template integer seq_nr := ?,
516 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200517 template Expires expires := *,
518 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100519 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100520 allow := tr_AllowMethods(allow_methods),
521 callId := {
522 fieldName := CALL_ID_E,
523 callid := call_id
524 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100525 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100526 contentType := content_type,
527 cSeq := {
528 fieldName := CSEQ_E,
529 seqNumber := seq_nr,
530 method := method
531 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100532 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100533 fromField := {
534 fieldName := FROM_E,
535 addressField := from_addr.addr,
536 fromParams := from_addr.params
537 },
538 toField := {
539 fieldName := TO_E,
540 addressField := to_addr.addr,
541 toParams := to_addr.params
542 },
543 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200544 via := via,
545 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100546}
547
548
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100549template (value) PDU_SIP_Request
550ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
551 template (value) CallidString call_id,
552 template (value) SipAddr from_addr,
553 template (value) SipAddr to_addr,
554 template (value) Via via,
555 integer seq_nr,
556 template (omit) Contact contact,
557 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200558 template (omit) Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100559 template (omit) charstring body := omit) := {
560 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
561 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
562 "REGISTER", seq_nr, via,
563 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200564 authorization := authorization,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100565 expires := expires),
566 messageBody := body,
567 payload := omit
568}
569template (present) PDU_SIP_Request
570tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
571 template (present) CallidString call_id := ?,
572 template (present) SipAddr from_addr := ?,
573 template (present) SipAddr to_addr := ?,
574 template integer seq_nr := *,
575 template Contact contact := *,
576 template Expires expires := *,
577 template charstring body := *) := {
578 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
579 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200580 tr_Via_from(f_tr_HostPort_opt_defport(from_addr.addr.nameAddr.addrSpec.hostPort)),
Pau Espin Pedrol65dde832024-04-08 18:15:58 +0200581 "REGISTER", *, seq_nr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100582 expires := expires),
583 messageBody := body,
584 payload := omit
585}
586
587template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200588ts_SIP_INVITE(template (value) CallidString call_id,
589 template (value) SipAddr from_addr,
590 template (value) SipAddr to_addr,
591 template (value) Via via,
592 template (value) Contact contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100593 integer seq_nr,
594 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100595 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200596 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100597 "INVITE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200598 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100599 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100600 messageBody := body,
601 payload := omit
602}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100603template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200604tr_SIP_INVITE(template (present) SipUrl uri,
605 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100606 template SipAddr from_addr,
607 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200608 template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100609 template integer seq_nr,
610 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200611 requestLine := tr_SIP_ReqLine(INVITE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100612 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200613 via, "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100614 messageBody := body,
615 payload := omit
616}
617
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100618template (value) PDU_SIP_Request
619ts_SIP_BYE(CallidString call_id,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200620 template (value) SipAddr from_addr,
621 template (value) SipAddr to_addr,
622 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100623 integer seq_nr,
624 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100625 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
626 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200627 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100628 messageBody := body,
629 payload := omit
630}
631
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100632template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200633tr_SIP_BYE(template (present) SipUrl uri,
634 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100635 template SipAddr from_addr,
636 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200637 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100638 template integer seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200639 template charstring body := *) := {
640 requestLine := tr_SIP_ReqLine(BYE_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100641 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200642 via, "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100643 messageBody := body,
644 payload := omit
645}
646
647
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100648template (value) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200649ts_SIP_ACK(template (value) CallidString call_id,
650 template (value) SipAddr from_addr,
651 template (value) SipAddr to_addr,
652 template (value) Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100653 integer seq_nr,
654 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100655 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100656 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
657 ts_Contact_SipAddr(from_addr),
658 "ACK", seq_nr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200659 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100660 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100661 messageBody := body,
662 payload := omit
663}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100664template (present) PDU_SIP_Request
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200665tr_SIP_ACK(template (present) SipUrl uri,
666 template CallidString call_id,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100667 template SipAddr from_addr,
668 template SipAddr to_addr,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200669 template Via via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100670 template integer seq_nr,
671 template charstring body) := {
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200672 requestLine := tr_SIP_ReqLine(ACK_E, uri),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100673 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200674 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100675 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100676 messageBody := body,
677 payload := omit
678}
679
Pau Espin Pedrol32167d82024-04-10 13:14:51 +0200680template (present) PDU_SIP_Request
681tr_SIP_CANCEL(template (present) SipUrl uri,
682 template (present) CallidString call_id,
683 template (present) SipAddr from_addr,
684 template (present) SipAddr to_addr,
685 template (present) Via via,
686 template (present) integer seq_nr,
687 template charstring body := *) := {
688 requestLine := tr_SIP_ReqLine(CANCEL_E, uri),
689 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
690 via,
691 "CANCEL", *, seq_nr),
692 messageBody := body,
693 payload := omit
694}
695
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100696template (value) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200697ts_SIP_Response(template (value) CallidString call_id,
698 template (value) SipAddr from_addr,
699 template (value) SipAddr to_addr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100700 charstring method,
701 integer status_code,
702 integer seq_nr,
703 charstring reason,
704 Via via,
705 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100706 statusLine := ts_SIP_StatusLine(status_code, reason),
707 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
708 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
709 messageBody := body,
710 payload := omit
711}
712
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200713/* 180 Ringing */
714template (value) PDU_SIP_Response
715ts_SIP_Response_Ringing(
716 template (value) CallidString call_id,
717 template (value) SipAddr from_addr,
718 template (value) SipAddr to_addr,
719 Via via,
720 integer seq_nr,
721 charstring method := "INVITE",
722 template (omit) charstring body := omit) := {
723 statusLine := ts_SIP_StatusLine(180, "Ringing"),
724 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
725 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
726 messageBody := body,
727 payload := omit
728}
729
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100730template (present) PDU_SIP_Response
731tr_SIP_Response(template CallidString call_id,
732 template SipAddr from_addr,
733 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200734 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100735 template Contact contact,
736 template charstring method,
737 template integer status_code,
738 template integer seq_nr := ?,
739 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200740 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100741 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100742 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200743 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100744 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100745 messageBody := body,
746 payload := omit
747}
748
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200749/* Expect during first REGISTER/INVITE/... when authorization is required: */
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100750template (present) PDU_SIP_Response
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200751tr_SIP_Response_Unauthorized(
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100752 template CallidString call_id,
753 template SipAddr from_addr,
754 template SipAddr to_addr,
755 template (present) Via via := tr_Via_from(?),
756 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200757 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100758 template integer seq_nr := ?,
759 template charstring method := "REGISTER",
760 template integer status_code := 401,
761 template charstring reason := "Unauthorized",
762 template charstring body := *) := {
763 statusLine := tr_SIP_StatusLine(status_code, reason),
764 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
765 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200766 method, *, seq_nr,
767 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100768 messageBody := body,
769 payload := omit
770}
Harald Welteb0d93602018-03-20 18:09:34 +0100771
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +0200772/* 100 Trying */
773template (present) PDU_SIP_Response
774tr_SIP_Response_Trying(
775 template CallidString call_id,
776 template SipAddr from_addr,
777 template SipAddr to_addr,
778 template (present) Via via := tr_Via_from(?),
779 template integer seq_nr := ?,
780 template charstring method := "INVITE",
781 template integer status_code := 100,
782 template charstring reason := "Trying",
783 template charstring body := *) := {
784 statusLine := tr_SIP_StatusLine(status_code, reason),
785 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
786 via,
787 method, *, seq_nr),
788 messageBody := body,
789 payload := omit
790}
791
792/* 180 Ringing */
793template (present) PDU_SIP_Response
794tr_SIP_Response_Ringing(
795 template CallidString call_id,
796 template SipAddr from_addr,
797 template SipAddr to_addr,
798 template (present) Via via := tr_Via_from(?),
799 template integer seq_nr := ?,
800 template charstring method := "INVITE",
801 template integer status_code := 180,
802 template charstring reason := "Ringing",
803 template charstring body := *) := {
804 statusLine := tr_SIP_StatusLine(status_code, reason),
805 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
806 via,
807 method, *, seq_nr),
808 messageBody := body,
809 payload := omit
810}
811
812/****************
813 * FUNCTIONS:
814 ****************/
815
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200816function f_sip_param_find(GenericParam_List li,
817 template (present) charstring id := ?)
818return template (omit) GenericParam {
819 var integer i;
820
821 for (i := 0; i < lengthof(li); i := i + 1) {
822 if (not ispresent(li[i])) {
823 continue;
824 }
825 if (match(li[i].id, id)) {
826 return li[i];
827 }
828 }
829 return omit;
830}
831
832function f_sip_param_find_or_fail(GenericParam_List li,
833 template (present) charstring id := ?)
834return GenericParam {
835 var template (omit) GenericParam parameter;
836 parameter := f_sip_param_find(li, id);
837 if (istemplatekind(parameter, "omit")) {
838 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
839 log2str("Param ", id, " not found in ", li));
840 }
841 return valueof(parameter);
842}
843
844function f_sip_param_get_value(GenericParam_List li,
845 template (present) charstring id := ?)
846return template (omit) charstring {
847 var template (omit) GenericParam parameter;
848 parameter := f_sip_param_find(li, id);
849 if (istemplatekind(parameter, "omit")) {
850 return omit;
851 }
852 return parameter.paramValue;
853}
854
855function f_sip_param_get_value_or_fail(GenericParam_List li,
856 template (present) charstring id := ?)
857return template (omit) charstring {
858 var GenericParam parameter;
859 parameter := f_sip_param_find_or_fail(li, id);
860 return parameter.paramValue;
861}
862
863function f_sip_param_get_value_present_or_fail(GenericParam_List li,
864 template (present) charstring id := ?)
865return charstring {
866 var GenericParam parameter;
867 parameter := f_sip_param_find_or_fail(li, id);
868 if (not ispresent(parameter.paramValue)) {
869 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
870 log2str("Param ", id, " value not present in ", li));
871 }
872 return parameter.paramValue;
873}
874
875function f_sip_param_match_value(GenericParam_List li,
876 template (present) charstring id := ?,
877 template charstring exp_paramValue := *)
878return boolean {
879 var template (omit) charstring val;
880 val := f_sip_param_get_value_or_fail(li, id);
881 if (istemplatekind(val, "omit")) {
882 return istemplatekind(val, "omit") or istemplatekind(val, "*");
883 }
884 return match(valueof(val), exp_paramValue);
885}
886
887function f_sip_param_match_value_or_fail(GenericParam_List li,
888 template (present) charstring id := ?,
889 template charstring exp_paramValue := *)
890{
891 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
892 if (istemplatekind(val, "omit")) {
893 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
894 return;
895 } else {
896 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
897 log2str("Param ", id, " match failed: val ", val,
898 " vs exp ", exp_paramValue));
899 }
900 }
901 if (not match(valueof(val), exp_paramValue)) {
902 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
903 log2str("Param ", id, " match failed: val ", val,
904 " vs exp ", exp_paramValue));
905 }
906}
907
908function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
909return GenericParam_List {
910 var integer i;
911 var GenericParam_List li;
912 var GenericParam_List new_li := {};
913
914 if (istemplatekind(li_tpl, "omit")) {
915 return {};
916 }
917
918 li := valueof(li_tpl);
919 for (i := 0; i < lengthof(li); i := i + 1) {
920 if (not ispresent(li[i]) or
921 not match(li[i].id, id)) {
922 new_li := new_li & {li[i]};
923 }
924 }
925 return new_li;
926}
927
928function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
929return GenericParam_List {
930 var integer i;
931 var GenericParam_List li;
932 var GenericParam_List new_li := {};
933 var boolean found := false;
934
935 if (istemplatekind(li_tpl, "omit")) {
936 return { valueof(ts_Param(id, val)) };
937 }
938
939 li := valueof(li_tpl);
940 for (i := 0; i < lengthof(li); i := i + 1) {
941 if (not ispresent(li[i]) or
942 not match(li[i].id, id)) {
943 new_li := new_li & {li[i]};
944 continue;
945 }
946 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
947 found := true;
948 }
949
950 if (not found) {
951 new_li := new_li & { valueof(ts_Param(id, val)) };
952 }
953 return new_li;
954}
955
956/* Make sure string is quoted. */
957function f_sip_str_quote(template (value) charstring val) return charstring {
958 var charstring str := valueof(val);
959 if (lengthof(str) == 0) {
960 return "";
961 }
962
963 if (str[0] != "\"") {
964 return "\"" & str & "\"";
965 }
966 return str;
967}
968
969/* Make sure string is unquoted.
970 * Similar to unq() in RFC 2617 */
971function f_sip_str_unquote(template (value) charstring val) return charstring {
972 var charstring str := valueof(val);
973 var integer len := lengthof(str);
974
975 if (len <= 1) {
976 return str;
977 }
978
979 if (str[0] == "\"" and str[len - 1] == "\"") {
980 return substr(str, 1, len - 2);
981 }
982 return str;
983}
984
985/* RFC 2617 3.2.2.2 A1 */
986function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
987
988 /* RFC 2617 3.2.2.2 A1 */
989 var charstring A1 := f_sip_str_unquote(user) & ":" &
990 f_sip_str_unquote(realm) & ":" &
991 password;
992 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
993 log("A1: md5('", A1, "') = ", digestA1);
994 return digestA1;
995}
996
997/* RFC 2617 3.2.2.2 A2 */
998function f_sip_digest_A2(charstring method, charstring uri) return charstring {
999
1000 var charstring A2 := method & ":" & uri
1001 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
1002 log("A2: md5('", A2, "') = ", digestA2);
1003 return digestA2;
1004}
1005
1006/* RFC 2617 3.2.2.1 Request-Digest */
1007function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
1008 charstring nc, charstring cnonce,
1009 charstring qop, charstring digestA2) return charstring {
1010 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
1011 nc & ":" &
1012 cnonce & ":" &
1013 f_sip_str_unquote(qop) & ":" &
1014 digestA2;
1015 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
1016 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
1017 return req_digest;
1018}
1019
1020/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
1021 * KD(secret, data) = H(concat(secret, ":", data))
1022 */
1023function f_sip_digest_KD(charstring secret, charstring data) return charstring {
1024 return f_str_tolower(f_calculateMD5(secret & ":" & data));
1025}
1026
1027/* Digest Auth: RFC 2617 */
1028function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
1029 charstring user, charstring password,
1030 charstring method, charstring uri,
1031 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
1032 var CommaParam_List digestCln;
1033 var template (value) Authorization authorization;
1034 var template (value) Credentials cred;
1035 var template (omit) GenericParam rx_param;
1036
1037 digestCln := www_authenticate.challenge[0].digestCln;
1038
1039 var charstring algorithm;
1040 rx_param := f_sip_param_find(digestCln, "algorithm");
1041 if (istemplatekind(rx_param, "omit")) {
1042 /* Assume MD5 if not set */
1043 algorithm := "MD5"
1044 } else {
1045 algorithm := valueof(rx_param.paramValue);
1046 if (f_strstr(algorithm, "MD5") == -1) {
1047 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
1048 log2str("Unexpected algorithm: ", algorithm));
1049 }
1050 }
1051
1052 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
1053 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
1054 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
1055 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
1056
1057 if (f_strstr(qop, "auth") == -1) {
1058 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
1059 }
1060 var charstring selected_qop := "auth";
1061
1062 /* RFC 2617 3.2.2.2 A1 */
1063 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
1064 /* RFC 2617 3.2.2.3 A2 */
1065 var charstring digestA2 := f_sip_digest_A2(method, uri);
1066
1067 /* RFC 2617 3.2.2.1 Request-Digest */
1068 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
1069 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
1070 nc, cnonce,
1071 selected_qop, digestA2);
1072
1073 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
1074 uri, req_digest,
1075 opaque, algorithm, selected_qop, cnonce, nc);
1076
1077 authorization := ts_Authorization(cred);
1078 return valueof(authorization);
1079}
1080
1081/* RFC 2617 3.5 Example */
1082function f_sip_digest_selftest() {
1083/*
1084The following example assumes that an access-protected document is
1085being requested from the server via a GET request. The URI of the
1086document is "http://www.nowhere.org/dir/index.html". Both client and
1087server know that the username for this document is "Mufasa", and the
1088password is "Circle Of Life" (with one space between each of the
1089three words).
1090
1091HTTP/1.1 401 Unauthorized
1092WWW-Authenticate: Digest
1093 realm="testrealm@host.com",
1094 qop="auth,auth-int",
1095 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1096 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1097
1098Authorization: Digest username="Mufasa",
1099 realm="testrealm@host.com",
1100 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
1101 uri="/dir/index.html",
1102 qop=auth,
1103 nc=00000001,
1104 cnonce="0a4f113b",
1105 response="6629fae49393a05397450978507c4ef1",
1106 opaque="5ccc069c403ebaf9f0171e9517f40e41"
1107*/
1108 var template (value) CommaParam_List digestCln := {
1109 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
1110 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
1111 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
1112 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
1113 };
1114 var template (value) WwwAuthenticate www_authenticate :=
1115 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
1116
1117 var Authorization authorization :=
1118 f_sip_digest_gen_Authorization(valueof(www_authenticate),
1119 "Mufasa",
1120 "Circle Of Life",
1121 "GET",
1122 "/dir/index.html",
1123 cnonce := "0a4f113b",
1124 nc_int := 1);
1125
1126 var CommaParam_List digestResp := authorization.body.digestResponse;
1127 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
1128 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
1129 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
1130 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
1131 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
1132 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
1133 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
1134 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
1135}
1136
Pau Espin Pedrol6052a342024-03-28 20:20:46 +01001137/* RFC 3261 8.1.1.5:
1138 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
1139 * and MUST be less than 2**31."
1140 */
1141function f_sip_rand_seq_nr() return integer {
1142 /* 2**31 = 2147483648 */
1143 return f_rnd_int(2147483648)
1144}
Harald Welteb0d93602018-03-20 18:09:34 +01001145
Pau Espin Pedrol7011bf42024-04-08 17:56:58 +02001146function f_sip_next_seq_nr(integer seq_nr) return integer {
1147 return (seq_nr + 1) mod 2147483648;
1148}
1149
1150function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) {
1151 req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber));
1152}
1153
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001154/* Tags shall have at least 32 bit of randomness */
1155function f_sip_rand_tag() return charstring {
1156 var integer rnd_int := f_rnd_int(4294967296);
1157 return hex2str(int2hex(rnd_int, 8));
1158}
1159
1160/* Generate a "branch" tag value.
1161 * RFC 3261 p.105 section 8:
1162 * "A common way to create this value is to compute a
1163 * cryptographic hash of the To tag, From tag, Call-ID header
1164 * field, the Request-URI of the request received (before
1165 * translation), the topmost Via header, and the sequence number
1166 * from the CSeq header field, in addition to any Proxy-Require
1167 * and Proxy-Authorization header fields that may be present. The
1168 * algorithm used to compute the hash is implementation-dependent,
1169 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1170 * choice."
1171 * See also Section 8.1.1.7:
1172 * "The branch ID inserted by an element compliant with this
1173 * specification MUST always begin with the characters "z9hG4bK"."
1174 */
1175const charstring sip_magic_cookie := "z9hG4bK";
1176function f_sip_gen_branch(charstring tag_to,
1177 charstring tag_from,
1178 charstring tag_call_id,
1179 integer cseq) return charstring {
1180 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1181 var charstring hash := f_calculateMD5(str);
1182 var charstring branch := sip_magic_cookie & hash;
1183 return branch;
1184}
1185
1186function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1187 var charstring str := "";
1188 if (ispresent(host_port.host)) {
1189 str := host_port.host;
1190 }
1191 if (ispresent(host_port.portField)) {
1192 str := str & ":" & int2str(host_port.portField);
1193 }
1194 return str;
1195}
1196
1197function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1198 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1199 return str;
1200}
1201
1202function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1203 if (ispresent(naddr.displayName)) {
1204 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1205 } else {
1206 return f_sip_SipUrl_to_str(naddr.addrSpec);
1207 }
1208}
1209
1210function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1211 if (ischosen(sip_addr.addr.nameAddr)) {
1212 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1213 } else {
1214 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1215 }
1216}
1217
Harald Welteb0d93602018-03-20 18:09:34 +01001218}