blob: 7cb9d68d9ceefc4140535bc66c3793f8c0a991a1 [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 Pedrolfb34d862024-03-28 20:21:38 +010055template (value) SipUrl ts_SipUrlHost(template (value) charstring host)
56 := ts_SipUrl(ts_HostPort(host));
57
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +020058template (value) Credentials ts_Credentials_DigestResponse(template (value) CommaParam_List digestResponse) := {
59 digestResponse := digestResponse
60}
61
62template (value) Credentials ts_Credentials_DigestResponseMD5(
63 template (value) charstring username,
64 template (value) charstring realm,
65 template (value) charstring nonce,
66 template (value) charstring uri,
67 template (value) charstring response,
68 template (value) charstring opaque,
69 template (value) charstring algorithm := "MD5",
70 template (value) charstring qop := "auth",
71 template (omit) charstring cnonce := omit,
72 template (omit) charstring nc := omit
73 ) := {
74 digestResponse := {
75 // Already added by digestResponse automatically:
76 //ts_Param("Digest", omit),
77 ts_Param("username", f_sip_str_quote(username)),
78 ts_Param("realm", f_sip_str_quote(realm)),
79 ts_Param("nonce", f_sip_str_quote(nonce)),
80 ts_Param("uri", f_sip_str_quote(uri)),
81 ts_Param("response", f_sip_str_quote(response)),
82 ts_Param("opaque", f_sip_str_quote(opaque)),
83 ts_Param("algorithm", algorithm),
84 ts_Param("qop", qop),
85 // FIXME: If "omit" is passed, these below end up in;
86 // "Dynamic test case error: Performing a valueof or send operation on a non-specific template of type @SIPmsg_Types.GenericParam"
87 f_ts_Param_omit("cnonce", f_sip_str_quote(cnonce)),
88 f_ts_Param_omit("nc", nc)
89 }
90}
91
92template (value) Credentials ts_Credentials_OtherAuth(template (value) OtherAuth otherResponse) := {
93 otherResponse := otherResponse
94}
95
96template (value) Authorization ts_Authorization(template (value) Credentials body) := {
97 fieldName := AUTHORIZATION_E,
98 body := body
99}
100
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100101// [20.10]
102template (present) NameAddr tr_NameAddr(template (present) SipUrl addrSpec := ?,
103 template charstring displayName := *) := {
104 displayName := displayName,
105 addrSpec := addrSpec
Harald Welteb0d93602018-03-20 18:09:34 +0100106}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100107template (value) NameAddr ts_NameAddr(template (value) SipUrl addrSpec,
108 template (omit) charstring displayName := omit) := {
109 displayName := displayName,
110 addrSpec := addrSpec
111}
112
113template (present) Addr_Union tr_Addr_Union_NameAddr(template (present) NameAddr nameAddr := ?) := {
114 nameAddr := nameAddr
115}
116template (value) Addr_Union ts_Addr_Union_NameAddr(template (value) NameAddr nameAddr) := {
117 nameAddr := nameAddr
118}
119
120template (present) Addr_Union tr_Addr_Union_SipUrl(template (present) SipUrl sipUrl := ?) := {
121 addrSpecUnion := sipUrl
122}
123template (value) Addr_Union ts_Addr_Union_SipUrl(template (value) SipUrl sipUrl) := {
124 addrSpecUnion := sipUrl
125}
126
127
128template (present) ContactAddress tr_ContactAddress(template (present) Addr_Union addressField := ?,
129 template SemicolonParam_List contactParams := *) := {
130 addressField := addressField,
131 contactParams := contactParams
132}
133template (value) ContactAddress ts_ContactAddress(template (value) Addr_Union addressField,
134 template (omit) SemicolonParam_List contactParams := omit) := {
135 addressField := addressField,
136 contactParams := contactParams
137}
138
139template (present) Contact tr_Contact(template (present) ContactAddress_List contactAddresses := ?) := {
140 fieldName := CONTACT_E,
141 contactBody := {
142 contactAddresses := contactAddresses
143 }
144}
145template (value) Contact ts_Contact(template (value) ContactAddress_List contactAddresses) := {
146 fieldName := CONTACT_E,
147 contactBody := {
148 contactAddresses := contactAddresses
149 }
150}
151
152template (value) Contact ts_ContactWildcard := {
153 fieldName := CONTACT_E,
154 contactBody := {
155 wildcard := "*"
156 }
157}
158
159template (present) Contact tr_Contact_SipAddr(template (present) SipAddr contact_addr := ?)
160 := tr_Contact({ tr_ContactAddress(contact_addr.addr, contact_addr.params) });
161
162private function f_tr_Contact_SipAddr(template SipAddr contact_addr) return template Contact
163{
164 if (istemplatekind(contact_addr, "omit")) {
165 return omit;
166 } else if (istemplatekind(contact_addr, "*")) {
167 return *;
168 }
169 return tr_Contact_SipAddr(contact_addr);
170}
171
172template (value) Contact ts_Contact_SipAddr(template (value) SipAddr contact_addr)
173 := ts_Contact({ ts_ContactAddress(contact_addr.addr, contact_addr.params) });
174private function ts_Contact_SipAddr_omit(template (omit) SipAddr contact_addr := omit) return template (omit) Contact
175{
176 if (istemplatekind(contact_addr, "omit")) {
177 return omit;
178 }
179 return ts_Contact_SipAddr(contact_addr);
180}
181
182
183// [20.19]
184template (value) Expires ts_Expires(template (value) DeltaSec deltaSec := "7200") := {
185 fieldName := EXPIRES_E,
186 deltaSec := deltaSec
187}
188
189template (value) SipAddr ts_SipAddr(template (value) HostPort host_port,
190 template (omit) UserInfo user_info := omit,
191 template (omit) charstring displayName := omit,
192 template (omit) SemicolonParam_List params := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100193 addr := {
194 nameAddr := {
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100195 displayName := displayName,
196 addrSpec := ts_SipUrl(host_port, user_info)
Harald Welteb0d93602018-03-20 18:09:34 +0100197 }
198 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100199 params := params
200}
201template (present) SipAddr tr_SipAddr(template (present) HostPort host_port := ?,
202 template UserInfo user_info := *,
203 template charstring displayName := *,
204 template SemicolonParam_List params := *) := {
205 addr := {
206 nameAddr := {
207 displayName := displayName,
208 addrSpec := tr_SipUrl(host_port, user_info)
209 }
210 },
211 params := params
Harald Welteb0d93602018-03-20 18:09:34 +0100212}
213
214/* build a receive template from a value: substitute '*' for omit */
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100215function tr_SipAddr_from_val(SipAddr tin) return template (present) SipAddr {
216 var template (present) SipAddr ret := tin;
Harald Welteb0d93602018-03-20 18:09:34 +0100217 if (tin.addr.nameAddr.displayName == omit) {
218 ret.addr.nameAddr.displayName := *;
219 }
220 if (tin.addr.nameAddr.addrSpec.userInfo.password == omit) {
221 ret.addr.nameAddr.addrSpec.userInfo.password := *;
222 }
223 if (tin.params == omit) {
224 ret.params := *;
225 }
226 return ret;
227}
228
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100229template (value) HostPort ts_HostPort(template (omit) charstring host := omit,
230 template (omit) integer portField := omit) := {
231 host := host,
232 portField := portField
233}
Harald Welteb0d93602018-03-20 18:09:34 +0100234function tr_HostPort(template HostPort hp) return template HostPort {
235 var template HostPort hpout := hp;
236 /* if the port number is 5060, it may be omitted */
237 if (isvalue(hp.portField) and valueof(hp.portField) == 5060) {
238 hpout.portField := 5060 ifpresent;
239 }
240 return hpout;
241}
242
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100243
244template (value) UserInfo ts_UserInfo(template (value) charstring userOrTelephoneSubscriber,
245 template (omit) charstring password := omit) := {
246 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
247 password := password
248}
249template (present) UserInfo tr_UserInfo(template (present) charstring userOrTelephoneSubscriber := ?,
250 template charstring password := *) := {
251 userOrTelephoneSubscriber := userOrTelephoneSubscriber,
252 password := password
253}
254
255template (value) RequestLine ts_SIP_ReqLine(Method method,
256 template (value) SipUrl uri,
Harald Welteb0d93602018-03-20 18:09:34 +0100257 charstring ver := c_SIP_VERSION) := {
258 method := method,
259 requestUri := uri,
260 sipVersion := ver
261}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100262template (present) RequestLine tr_SIP_ReqLine(template (present) Method method := ?,
263 template (present) SipUrl uri := ?,
264 template (present) charstring ver := c_SIP_VERSION) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100265 method := method,
266 requestUri := uri,
267 sipVersion := ver
268}
269
270template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := {
271 sipVersion := "SIP/2.0",
272 statusCode := status_code,
273 reasonPhrase := reason
274}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100275template (present) StatusLine tr_SIP_StatusLine(template integer status_code,
276 template charstring reason) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100277 sipVersion := "SIP/2.0",
278 statusCode := status_code,
279 reasonPhrase := reason
280}
281
282
283template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := {
284 requestLine := rl,
285 msgHeader := c_SIP_msgHeader_empty,
286 messageBody := omit,
287 payload := omit
288}
289
290const Method_List c_SIP_defaultMethods := {
291 "INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE",
292 "NOTIFY", "REFER", "UPDATE" };
293
294private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body)
295return template (omit) ContentType {
296 /* if user explicitly stated no content type */
297 if (istemplatekind(ct, "omit")) {
298 return omit;
299 }
300 /* if there's no body, then there's no content-type either */
301 if (istemplatekind(body, "omit")) {
302 return omit;
303 }
304 return ct;
305}
306
307template (value) ContentType ts_CT_SDP := {
308 fieldName := CONTENT_TYPE_E,
309 mediaType := "application/sdp"
310};
311
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100312template (value) Via ts_Via_from(template (value) HostPort addr) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100313 fieldName := VIA_E,
314 viaBody := {
315 {
316 sentProtocol := { "SIP", "2.0", "UDP" },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100317 sentBy := addr,
Harald Welteb0d93602018-03-20 18:09:34 +0100318 viaParams := omit
319 }
320 }
321}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100322template (present) Via tr_Via_from(template (present) HostPort host_port := ?,
323 template SemicolonParam_List viaParams := *) := {
324 fieldName := VIA_E,
325 viaBody := {
326 {
327 sentProtocol := { "SIP", "2.0", "UDP" },
328 sentBy := host_port,
329 viaParams := viaParams
330 }
331 }
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200332}
333
334template (present) OtherAuth
335tr_OtherAuth(template (present) charstring authScheme := ?,
336 template (present) CommaParam_List authParams := ?) := {
337 authScheme := authScheme,
338 authParams := authParams
339}
340
341template (value) OtherAuth
342ts_OtherAuth(template (value) charstring authScheme,
343 template (value) CommaParam_List authParams) := {
344 authScheme := authScheme,
345 authParams := authParams
346}
347
348template (present) Challenge
349tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := {
350 digestCln := digestCln
351}
352
353template (value) Challenge
354ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := {
355 digestCln := digestCln
356}
357
358template (present) Challenge
359tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := {
360 otherChallenge := otherChallenge
361}
362
363template (value) Challenge
364ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := {
365 otherChallenge := otherChallenge
366}
367
368template (present) WwwAuthenticate
369tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := {
370 fieldName := WWW_AUTHENTICATE_E,
371 challenge := challenge
372}
373
374template (value) WwwAuthenticate
375ts_WwwAuthenticate(template (value) Challenge_list challenge) := {
376 fieldName := WWW_AUTHENTICATE_E,
377 challenge := challenge
378}
Harald Welteb0d93602018-03-20 18:09:34 +0100379
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100380template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty;
381template (value) MessageHeader
382ts_SIP_msgh_std(template (value) CallidString call_id,
383 template (value) SipAddr from_addr,
384 template (value) SipAddr to_addr,
385 template (omit) Contact contact,
386 template (value) charstring method,
387 template (value) integer seq_nr,
388 template (value) Via via,
389 template (omit) ContentType content_type := omit,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200390 template (omit)Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100391 template (value) Method_List allow_methods := c_SIP_defaultMethods,
392 template (omit) Expires expires := omit
393 ) modifies ts_SIP_msgHeader_empty := {
Harald Welteb0d93602018-03-20 18:09:34 +0100394 allow := {
395 fieldName := ALLOW_E,
396 methods := allow_methods
397 },
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200398 authorization := authorization,
Harald Welteb0d93602018-03-20 18:09:34 +0100399 callId := {
400 fieldName := CALL_ID_E,
401 callid := call_id
402 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100403 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100404 contentType := content_type,
405 cSeq := {
406 fieldName := CSEQ_E,
407 seqNumber := seq_nr,
408 method := method
409 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100410 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100411 fromField := {
412 fieldName := FROM_E,
413 addressField := from_addr.addr,
414 fromParams := from_addr.params
415 },
416 toField := {
417 fieldName := TO_E,
418 addressField := to_addr.addr,
419 toParams := to_addr.params
420 },
421 userAgent := {
422 fieldName := USER_AGENT_E,
423 userAgentBody := {
424 "osmo-ttcn3-hacks/0.23"
425 }
426 },
427 via := via
428}
429
Harald Welteb0d93602018-03-20 18:09:34 +0100430function tr_AllowMethods(template Method_List allow_methods) return template Allow {
431 if (istemplatekind(allow_methods, "omit")) {
432 return omit;
433 } else if (istemplatekind(allow_methods, "*")) {
434 return *;
435 } else if (istemplatekind(allow_methods, "?")) {
436 return ?;
437 }
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100438 var template (present) Allow ret := {
Harald Welteb0d93602018-03-20 18:09:34 +0100439 fieldName := ALLOW_E,
440 methods := allow_methods
441 }
442 return ret
443}
444
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100445template (present) MessageHeader
446tr_SIP_msgh_std(template CallidString call_id,
447 template SipAddr from_addr,
448 template SipAddr to_addr,
449 template Contact contact,
450 template (present) Via via := tr_Via_from(?),
451 template charstring method,
452 template ContentType content_type := *,
453 template integer seq_nr := ?,
454 template Method_List allow_methods := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200455 template Expires expires := *,
456 template WwwAuthenticate wwwAuthenticate := *
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100457 ) modifies t_SIP_msgHeader_any := {
Harald Welteb0d93602018-03-20 18:09:34 +0100458 allow := tr_AllowMethods(allow_methods),
459 callId := {
460 fieldName := CALL_ID_E,
461 callid := call_id
462 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100463 contact := contact,
Harald Welteb0d93602018-03-20 18:09:34 +0100464 contentType := content_type,
465 cSeq := {
466 fieldName := CSEQ_E,
467 seqNumber := seq_nr,
468 method := method
469 },
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100470 expires := expires,
Harald Welteb0d93602018-03-20 18:09:34 +0100471 fromField := {
472 fieldName := FROM_E,
473 addressField := from_addr.addr,
474 fromParams := from_addr.params
475 },
476 toField := {
477 fieldName := TO_E,
478 addressField := to_addr.addr,
479 toParams := to_addr.params
480 },
481 userAgent := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200482 via := via,
483 wwwAuthenticate := wwwAuthenticate
Harald Welteb0d93602018-03-20 18:09:34 +0100484}
485
486
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100487template (value) PDU_SIP_Request
488ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port,
489 template (value) CallidString call_id,
490 template (value) SipAddr from_addr,
491 template (value) SipAddr to_addr,
492 template (value) Via via,
493 integer seq_nr,
494 template (omit) Contact contact,
495 template (omit) Expires expires,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200496 template (omit) Authorization authorization := omit,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100497 template (omit) charstring body := omit) := {
498 requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port),
499 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact,
500 "REGISTER", seq_nr, via,
501 f_ContentTypeOrOmit(ts_CT_SDP, body),
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200502 authorization := authorization,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100503 expires := expires),
504 messageBody := body,
505 payload := omit
506}
507template (present) PDU_SIP_Request
508tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?,
509 template (present) CallidString call_id := ?,
510 template (present) SipAddr from_addr := ?,
511 template (present) SipAddr to_addr := ?,
512 template integer seq_nr := *,
513 template Contact contact := *,
514 template Expires expires := *,
515 template charstring body := *) := {
516 requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port),
517 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
518 tr_Via_from(tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort)),
519 "INVITE", *, seq_nr,
520 expires := expires),
521 messageBody := body,
522 payload := omit
523}
524
525template (value) PDU_SIP_Request
526ts_SIP_INVITE(CallidString call_id,
527 SipAddr from_addr,
528 SipAddr to_addr,
529 integer seq_nr,
530 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100531 requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100532 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
533 ts_Contact_SipAddr(from_addr),
534 "INVITE", seq_nr,
535 ts_Via_from(from_addr.addr.nameAddr.addrSpec.hostPort),
536 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100537 messageBody := body,
538 payload := omit
539}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100540template (present) PDU_SIP_Request
541tr_SIP_INVITE(template CallidString call_id,
542 template SipAddr from_addr,
543 template SipAddr to_addr,
544 template integer seq_nr,
545 template charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100546 requestLine := tr_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100547 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?,
548 tr_Via_from(tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort)),
549 "INVITE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100550 messageBody := body,
551 payload := omit
552}
553
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100554template (value) PDU_SIP_Request
555ts_SIP_BYE(CallidString call_id,
556 SipAddr from_addr,
557 SipAddr to_addr,
558 integer seq_nr,
559 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100560 requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
561 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100562 ts_Via_from(from_addr.addr.nameAddr.addrSpec.hostPort),
563 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100564 messageBody := body,
565 payload := omit
566}
567
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100568template (present) PDU_SIP_Request
569tr_SIP_BYE(template CallidString call_id,
570 template SipAddr from_addr,
571 template SipAddr to_addr,
572 template integer seq_nr,
573 template charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100574 requestLine := tr_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100575 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit,
576 tr_Via_from(tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort)),
577 "BYE", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100578 messageBody := body,
579 payload := omit
580}
581
582
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100583template (value) PDU_SIP_Request
584ts_SIP_ACK(CallidString call_id,
585 SipAddr from_addr,
586 SipAddr to_addr,
587 integer seq_nr,
588 template (omit) charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100589 requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100590 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr,
591 ts_Contact_SipAddr(from_addr),
592 "ACK", seq_nr,
593 ts_Via_from(from_addr.addr.nameAddr.addrSpec.hostPort),
594 f_ContentTypeOrOmit(ts_CT_SDP, body)),
Harald Welteb0d93602018-03-20 18:09:34 +0100595 messageBody := body,
596 payload := omit
597}
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100598template (present) PDU_SIP_Request
599tr_SIP_ACK(template CallidString call_id,
600 template SipAddr from_addr,
601 template SipAddr to_addr,
602 template integer seq_nr,
603 template charstring body) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100604 requestLine := tr_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100605 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *,
606 tr_Via_from(tr_HostPort(from_addr.addr.nameAddr.addrSpec.hostPort)),
607 "ACK", *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100608 messageBody := body,
609 payload := omit
610}
611
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100612template (value) PDU_SIP_Response
613ts_SIP_Response(CallidString call_id,
614 SipAddr from_addr,
615 SipAddr to_addr,
616 charstring method,
617 integer status_code,
618 integer seq_nr,
619 charstring reason,
620 Via via,
621 template (omit) charstring body := omit) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100622 statusLine := ts_SIP_StatusLine(status_code, reason),
623 msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr,
624 via, f_ContentTypeOrOmit(ts_CT_SDP, body)),
625 messageBody := body,
626 payload := omit
627}
628
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100629template (present) PDU_SIP_Response
630tr_SIP_Response(template CallidString call_id,
631 template SipAddr from_addr,
632 template SipAddr to_addr,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200633 template (present) Via via := tr_Via_from(?),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100634 template Contact contact,
635 template charstring method,
636 template integer status_code,
637 template integer seq_nr := ?,
638 template charstring reason := ?,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200639 template charstring body := *) := {
Harald Welteb0d93602018-03-20 18:09:34 +0100640 statusLine := tr_SIP_StatusLine(status_code, reason),
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100641 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200642 via,
Pau Espin Pedrolfb34d862024-03-28 20:21:38 +0100643 method, *, seq_nr),
Harald Welteb0d93602018-03-20 18:09:34 +0100644 messageBody := body,
645 payload := omit
646}
647
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100648/* Expect during first REGISTER when authorization is required: */
649template (present) PDU_SIP_Response
650tr_SIP_Response_REGISTER_Unauthorized(
651 template CallidString call_id,
652 template SipAddr from_addr,
653 template SipAddr to_addr,
654 template (present) Via via := tr_Via_from(?),
655 template Contact contact := *,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200656 template (present) WwwAuthenticate wwwAuthenticate := ?,
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100657 template integer seq_nr := ?,
658 template charstring method := "REGISTER",
659 template integer status_code := 401,
660 template charstring reason := "Unauthorized",
661 template charstring body := *) := {
662 statusLine := tr_SIP_StatusLine(status_code, reason),
663 msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact,
664 via,
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200665 method, *, seq_nr,
666 wwwAuthenticate := wwwAuthenticate),
Pau Espin Pedrol37ee0ed2024-03-28 21:17:12 +0100667 messageBody := body,
668 payload := omit
669}
Harald Welteb0d93602018-03-20 18:09:34 +0100670
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +0200671function f_sip_param_find(GenericParam_List li,
672 template (present) charstring id := ?)
673return template (omit) GenericParam {
674 var integer i;
675
676 for (i := 0; i < lengthof(li); i := i + 1) {
677 if (not ispresent(li[i])) {
678 continue;
679 }
680 if (match(li[i].id, id)) {
681 return li[i];
682 }
683 }
684 return omit;
685}
686
687function f_sip_param_find_or_fail(GenericParam_List li,
688 template (present) charstring id := ?)
689return GenericParam {
690 var template (omit) GenericParam parameter;
691 parameter := f_sip_param_find(li, id);
692 if (istemplatekind(parameter, "omit")) {
693 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
694 log2str("Param ", id, " not found in ", li));
695 }
696 return valueof(parameter);
697}
698
699function f_sip_param_get_value(GenericParam_List li,
700 template (present) charstring id := ?)
701return template (omit) charstring {
702 var template (omit) GenericParam parameter;
703 parameter := f_sip_param_find(li, id);
704 if (istemplatekind(parameter, "omit")) {
705 return omit;
706 }
707 return parameter.paramValue;
708}
709
710function f_sip_param_get_value_or_fail(GenericParam_List li,
711 template (present) charstring id := ?)
712return template (omit) charstring {
713 var GenericParam parameter;
714 parameter := f_sip_param_find_or_fail(li, id);
715 return parameter.paramValue;
716}
717
718function f_sip_param_get_value_present_or_fail(GenericParam_List li,
719 template (present) charstring id := ?)
720return charstring {
721 var GenericParam parameter;
722 parameter := f_sip_param_find_or_fail(li, id);
723 if (not ispresent(parameter.paramValue)) {
724 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
725 log2str("Param ", id, " value not present in ", li));
726 }
727 return parameter.paramValue;
728}
729
730function f_sip_param_match_value(GenericParam_List li,
731 template (present) charstring id := ?,
732 template charstring exp_paramValue := *)
733return boolean {
734 var template (omit) charstring val;
735 val := f_sip_param_get_value_or_fail(li, id);
736 if (istemplatekind(val, "omit")) {
737 return istemplatekind(val, "omit") or istemplatekind(val, "*");
738 }
739 return match(valueof(val), exp_paramValue);
740}
741
742function f_sip_param_match_value_or_fail(GenericParam_List li,
743 template (present) charstring id := ?,
744 template charstring exp_paramValue := *)
745{
746 var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id);
747 if (istemplatekind(val, "omit")) {
748 if (istemplatekind(val, "omit") or istemplatekind(val, "*")) {
749 return;
750 } else {
751 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
752 log2str("Param ", id, " match failed: val ", val,
753 " vs exp ", exp_paramValue));
754 }
755 }
756 if (not match(valueof(val), exp_paramValue)) {
757 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
758 log2str("Param ", id, " match failed: val ", val,
759 " vs exp ", exp_paramValue));
760 }
761}
762
763function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id)
764return GenericParam_List {
765 var integer i;
766 var GenericParam_List li;
767 var GenericParam_List new_li := {};
768
769 if (istemplatekind(li_tpl, "omit")) {
770 return {};
771 }
772
773 li := valueof(li_tpl);
774 for (i := 0; i < lengthof(li); i := i + 1) {
775 if (not ispresent(li[i]) or
776 not match(li[i].id, id)) {
777 new_li := new_li & {li[i]};
778 }
779 }
780 return new_li;
781}
782
783function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val)
784return GenericParam_List {
785 var integer i;
786 var GenericParam_List li;
787 var GenericParam_List new_li := {};
788 var boolean found := false;
789
790 if (istemplatekind(li_tpl, "omit")) {
791 return { valueof(ts_Param(id, val)) };
792 }
793
794 li := valueof(li_tpl);
795 for (i := 0; i < lengthof(li); i := i + 1) {
796 if (not ispresent(li[i]) or
797 not match(li[i].id, id)) {
798 new_li := new_li & {li[i]};
799 continue;
800 }
801 new_li := new_li & { valueof(ts_Param(li[i].id, val)) };
802 found := true;
803 }
804
805 if (not found) {
806 new_li := new_li & { valueof(ts_Param(id, val)) };
807 }
808 return new_li;
809}
810
811/* Make sure string is quoted. */
812function f_sip_str_quote(template (value) charstring val) return charstring {
813 var charstring str := valueof(val);
814 if (lengthof(str) == 0) {
815 return "";
816 }
817
818 if (str[0] != "\"") {
819 return "\"" & str & "\"";
820 }
821 return str;
822}
823
824/* Make sure string is unquoted.
825 * Similar to unq() in RFC 2617 */
826function f_sip_str_unquote(template (value) charstring val) return charstring {
827 var charstring str := valueof(val);
828 var integer len := lengthof(str);
829
830 if (len <= 1) {
831 return str;
832 }
833
834 if (str[0] == "\"" and str[len - 1] == "\"") {
835 return substr(str, 1, len - 2);
836 }
837 return str;
838}
839
840/* RFC 2617 3.2.2.2 A1 */
841function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring {
842
843 /* RFC 2617 3.2.2.2 A1 */
844 var charstring A1 := f_sip_str_unquote(user) & ":" &
845 f_sip_str_unquote(realm) & ":" &
846 password;
847 var charstring digestA1 := f_str_tolower(f_calculateMD5(A1));
848 log("A1: md5('", A1, "') = ", digestA1);
849 return digestA1;
850}
851
852/* RFC 2617 3.2.2.2 A2 */
853function f_sip_digest_A2(charstring method, charstring uri) return charstring {
854
855 var charstring A2 := method & ":" & uri
856 var charstring digestA2 := f_str_tolower(f_calculateMD5(A2));
857 log("A2: md5('", A2, "') = ", digestA2);
858 return digestA2;
859}
860
861/* RFC 2617 3.2.2.1 Request-Digest */
862function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce,
863 charstring nc, charstring cnonce,
864 charstring qop, charstring digestA2) return charstring {
865 var charstring digest_data := f_sip_str_unquote(nonce) & ":" &
866 nc & ":" &
867 cnonce & ":" &
868 f_sip_str_unquote(qop) & ":" &
869 digestA2;
870 var charstring req_digest := f_sip_digest_KD(digestA1, digest_data);
871 log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest);
872 return req_digest;
873}
874
875/* RFC 2617 3.2.1 The WWW-Authenticate Response Header
876 * KD(secret, data) = H(concat(secret, ":", data))
877 */
878function f_sip_digest_KD(charstring secret, charstring data) return charstring {
879 return f_str_tolower(f_calculateMD5(secret & ":" & data));
880}
881
882/* Digest Auth: RFC 2617 */
883function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate,
884 charstring user, charstring password,
885 charstring method, charstring uri,
886 charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization {
887 var CommaParam_List digestCln;
888 var template (value) Authorization authorization;
889 var template (value) Credentials cred;
890 var template (omit) GenericParam rx_param;
891
892 digestCln := www_authenticate.challenge[0].digestCln;
893
894 var charstring algorithm;
895 rx_param := f_sip_param_find(digestCln, "algorithm");
896 if (istemplatekind(rx_param, "omit")) {
897 /* Assume MD5 if not set */
898 algorithm := "MD5"
899 } else {
900 algorithm := valueof(rx_param.paramValue);
901 if (f_strstr(algorithm, "MD5") == -1) {
902 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
903 log2str("Unexpected algorithm: ", algorithm));
904 }
905 }
906
907 var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm");
908 var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce");
909 var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque");
910 var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop");
911
912 if (f_strstr(qop, "auth") == -1) {
913 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop));
914 }
915 var charstring selected_qop := "auth";
916
917 /* RFC 2617 3.2.2.2 A1 */
918 var charstring digestA1 := f_sip_digest_A1(user, realm, password);
919 /* RFC 2617 3.2.2.3 A2 */
920 var charstring digestA2 := f_sip_digest_A2(method, uri);
921
922 /* RFC 2617 3.2.2.1 Request-Digest */
923 var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8)));
924 var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce,
925 nc, cnonce,
926 selected_qop, digestA2);
927
928 cred := ts_Credentials_DigestResponseMD5(user, realm, nonce,
929 uri, req_digest,
930 opaque, algorithm, selected_qop, cnonce, nc);
931
932 authorization := ts_Authorization(cred);
933 return valueof(authorization);
934}
935
936/* RFC 2617 3.5 Example */
937function f_sip_digest_selftest() {
938/*
939The following example assumes that an access-protected document is
940being requested from the server via a GET request. The URI of the
941document is "http://www.nowhere.org/dir/index.html". Both client and
942server know that the username for this document is "Mufasa", and the
943password is "Circle Of Life" (with one space between each of the
944three words).
945
946HTTP/1.1 401 Unauthorized
947WWW-Authenticate: Digest
948 realm="testrealm@host.com",
949 qop="auth,auth-int",
950 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
951 opaque="5ccc069c403ebaf9f0171e9517f40e41"
952
953Authorization: Digest username="Mufasa",
954 realm="testrealm@host.com",
955 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
956 uri="/dir/index.html",
957 qop=auth,
958 nc=00000001,
959 cnonce="0a4f113b",
960 response="6629fae49393a05397450978507c4ef1",
961 opaque="5ccc069c403ebaf9f0171e9517f40e41"
962*/
963 var template (value) CommaParam_List digestCln := {
964 ts_Param("realm", f_sip_str_quote("testrealm@host.com")),
965 ts_Param("qop", f_sip_str_quote("auth,auth-int")),
966 ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")),
967 ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"))
968 };
969 var template (value) WwwAuthenticate www_authenticate :=
970 ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } )
971
972 var Authorization authorization :=
973 f_sip_digest_gen_Authorization(valueof(www_authenticate),
974 "Mufasa",
975 "Circle Of Life",
976 "GET",
977 "/dir/index.html",
978 cnonce := "0a4f113b",
979 nc_int := 1);
980
981 var CommaParam_List digestResp := authorization.body.digestResponse;
982 f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com"));
983 f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093"));
984 f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html"));
985 f_sip_param_match_value_or_fail(digestResp, "qop", "auth");
986 f_sip_param_match_value_or_fail(digestResp, "nc", "00000001");
987 f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b"));
988 f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1"));
989 f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41"));
990}
991
Pau Espin Pedrol6052a342024-03-28 20:20:46 +0100992/* RFC 3261 8.1.1.5:
993 * "The sequence number value MUST be expressible as a 32-bit unsigned integer
994 * and MUST be less than 2**31."
995 */
996function f_sip_rand_seq_nr() return integer {
997 /* 2**31 = 2147483648 */
998 return f_rnd_int(2147483648)
999}
Harald Welteb0d93602018-03-20 18:09:34 +01001000
Pau Espin Pedrol05eaa1a2024-04-02 12:56:26 +02001001/* Tags shall have at least 32 bit of randomness */
1002function f_sip_rand_tag() return charstring {
1003 var integer rnd_int := f_rnd_int(4294967296);
1004 return hex2str(int2hex(rnd_int, 8));
1005}
1006
1007/* Generate a "branch" tag value.
1008 * RFC 3261 p.105 section 8:
1009 * "A common way to create this value is to compute a
1010 * cryptographic hash of the To tag, From tag, Call-ID header
1011 * field, the Request-URI of the request received (before
1012 * translation), the topmost Via header, and the sequence number
1013 * from the CSeq header field, in addition to any Proxy-Require
1014 * and Proxy-Authorization header fields that may be present. The
1015 * algorithm used to compute the hash is implementation-dependent,
1016 * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable
1017 * choice."
1018 * See also Section 8.1.1.7:
1019 * "The branch ID inserted by an element compliant with this
1020 * specification MUST always begin with the characters "z9hG4bK"."
1021 */
1022const charstring sip_magic_cookie := "z9hG4bK";
1023function f_sip_gen_branch(charstring tag_to,
1024 charstring tag_from,
1025 charstring tag_call_id,
1026 integer cseq) return charstring {
1027 var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq);
1028 var charstring hash := f_calculateMD5(str);
1029 var charstring branch := sip_magic_cookie & hash;
1030 return branch;
1031}
1032
1033function f_sip_HostPort_to_str(HostPort host_port) return charstring {
1034 var charstring str := "";
1035 if (ispresent(host_port.host)) {
1036 str := host_port.host;
1037 }
1038 if (ispresent(host_port.portField)) {
1039 str := str & ":" & int2str(host_port.portField);
1040 }
1041 return str;
1042}
1043
1044function f_sip_SipUrl_to_str(SipUrl uri) return charstring {
1045 var charstring str := uri.scheme & f_sip_HostPort_to_str(uri.hostPort);
1046 return str;
1047}
1048
1049function f_sip_NameAddr_to_str(NameAddr naddr) return charstring {
1050 if (ispresent(naddr.displayName)) {
1051 return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">";
1052 } else {
1053 return f_sip_SipUrl_to_str(naddr.addrSpec);
1054 }
1055}
1056
1057function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring {
1058 if (ischosen(sip_addr.addr.nameAddr)) {
1059 return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr);
1060 } else {
1061 return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion);
1062 }
1063}
1064
Harald Welteb0d93602018-03-20 18:09:34 +01001065}