module SIP_Templates { import from SIPmsg_Types all; import from TCCConversion_Functions all; import from TCCOpenSecurity_Functions all; import from TCCDateTime_Functions all; import from Native_Functions all; import from Osmocom_Types all; import from Misc_Helpers all; /* wrapper type to encapsulate the Addr_Union + parameter list used in From, To. ... */ type record SipAddr { Addr_Union addr, SemicolonParam_List params optional } const charstring c_SIP_VERSION := "SIP/2.0"; template (value) GenericParam ts_Param(template (value) charstring id, template (omit) charstring paramValue := omit) := { id := id, paramValue := paramValue } template (present) GenericParam tr_Param(template (present) charstring id := ?, template charstring paramValue := *) := { id := id, paramValue := paramValue } function f_ts_Param_omit(template (value) charstring id, template (omit) charstring paramValue := omit) return template (omit) GenericParam { if (istemplatekind(paramValue, "omit")) { return omit; } return ts_Param(id, paramValue); } template (value) SipUrl ts_SipUrl(template (value) HostPort host_port, template (omit) UserInfo user_info := omit) := { scheme := "sip", userInfo := user_info, hostPort := host_port, urlParameters := omit, headers := omit } template (present) SipUrl tr_SipUrl(template (present) HostPort host_port := ?, template UserInfo user_info := *) := { scheme := "sip", userInfo := user_info, hostPort := host_port, urlParameters := *, headers := * } template (value) SipUrl ts_SipUrlHost(template (value) charstring host, template (omit) integer portField := omit) := ts_SipUrl(ts_HostPort(host, portField)); function ts_SipUrl_from_Addr_Union(template (value) Addr_Union au) return template (value) SipUrl { if (ischosen(au.nameAddr)) { return au.nameAddr.addrSpec; } else { /* au.addrSpecUnion */ return au.addrSpecUnion; } } // [20.5] template (present) Allow tr_Allow(template Method_List methods := *) := { fieldName := ALLOW_E, methods := methods } template (value) Allow ts_Allow(template (omit) Method_List methods := omit) := { fieldName := ALLOW_E, methods := methods } template (present) Credentials tr_Credentials_DigestResponse(template (present) CommaParam_List digestResponse) := { digestResponse := digestResponse } template (value) Credentials ts_Credentials_DigestResponse(template (value) CommaParam_List digestResponse) := { digestResponse := digestResponse } template (value) Credentials ts_Credentials_DigestResponseMD5( template (value) charstring username, template (value) charstring realm, template (value) charstring nonce, template (value) charstring uri, template (value) charstring response, template (value) charstring opaque, template (value) charstring algorithm := "MD5", template (value) charstring qop := "auth", template (omit) charstring cnonce := omit, template (omit) charstring nc := omit ) := { digestResponse := { // Already added by digestResponse automatically: //ts_Param("Digest", omit), ts_Param("username", f_sip_str_quote(username)), ts_Param("realm", f_sip_str_quote(realm)), ts_Param("nonce", f_sip_str_quote(nonce)), ts_Param("uri", f_sip_str_quote(uri)), ts_Param("response", f_sip_str_quote(response)), ts_Param("opaque", f_sip_str_quote(opaque)), ts_Param("algorithm", algorithm), ts_Param("qop", qop), // FIXME: If "omit" is passed, these below end up in; // "Dynamic test case error: Performing a valueof or send operation on a non-specific template of type @SIPmsg_Types.GenericParam" f_ts_Param_omit("cnonce", f_sip_str_quote(cnonce)), f_ts_Param_omit("nc", nc) } } template (value) Credentials ts_Credentials_OtherAuth(template (value) OtherAuth otherResponse) := { otherResponse := otherResponse } template (present) Authorization tr_Authorization(template (present) Credentials body) := { fieldName := AUTHORIZATION_E, body := body } template (value) Authorization ts_Authorization(template (value) Credentials body) := { fieldName := AUTHORIZATION_E, body := body } // [20.10] template (present) NameAddr tr_NameAddr(template (present) SipUrl addrSpec := ?, template charstring displayName := *) := { displayName := displayName, addrSpec := addrSpec } template (value) NameAddr ts_NameAddr(template (value) SipUrl addrSpec, template (omit) charstring displayName := omit) := { displayName := displayName, addrSpec := addrSpec } template (present) Addr_Union tr_Addr_Union_NameAddr(template (present) NameAddr nameAddr := ?) := { nameAddr := nameAddr } template (value) Addr_Union ts_Addr_Union_NameAddr(template (value) NameAddr nameAddr) := { nameAddr := nameAddr } template (present) Addr_Union tr_Addr_Union_SipUrl(template (present) SipUrl sipUrl := ?) := { addrSpecUnion := sipUrl } template (value) Addr_Union ts_Addr_Union_SipUrl(template (value) SipUrl sipUrl) := { addrSpecUnion := sipUrl } template (present) ContactAddress tr_ContactAddress(template (present) Addr_Union addressField := ?, template SemicolonParam_List contactParams := *) := { addressField := addressField, contactParams := contactParams } template (value) ContactAddress ts_ContactAddress(template (value) Addr_Union addressField, template (omit) SemicolonParam_List contactParams := omit) := { addressField := addressField, contactParams := contactParams } template (present) Contact tr_Contact(template (present) ContactAddress_List contactAddresses := ?) := { fieldName := CONTACT_E, contactBody := { contactAddresses := contactAddresses } } template (value) Contact ts_Contact(template (value) ContactAddress_List contactAddresses) := { fieldName := CONTACT_E, contactBody := { contactAddresses := contactAddresses } } template (value) Contact ts_ContactWildcard := { fieldName := CONTACT_E, contactBody := { wildcard := "*" } } template (present) Contact tr_Contact_SipAddr(template (present) SipAddr contact_addr := ?) := tr_Contact({ tr_ContactAddress(contact_addr.addr, contact_addr.params) }); private function f_tr_Contact_SipAddr(template SipAddr contact_addr) return template Contact { if (istemplatekind(contact_addr, "omit")) { return omit; } else if (istemplatekind(contact_addr, "*")) { return *; } return tr_Contact_SipAddr(contact_addr); } template (value) Contact ts_Contact_SipAddr(template (value) SipAddr contact_addr) := ts_Contact({ ts_ContactAddress(contact_addr.addr, contact_addr.params) }); private function ts_Contact_SipAddr_omit(template (omit) SipAddr contact_addr := omit) return template (omit) Contact { if (istemplatekind(contact_addr, "omit")) { return omit; } return ts_Contact_SipAddr(contact_addr); } // [20.14] template (value) ContentLength ts_ContentLength(template (value) integer len := 0) := { fieldName := CONTENT_LENGTH_E, len := len } template (present) ContentLength tr_ContentLength(template (present) integer len := ?) := { fieldName := CONTENT_LENGTH_E, len := len } // [20.19] template (value) Expires ts_Expires(template (value) DeltaSec deltaSec := "7200") := { fieldName := EXPIRES_E, deltaSec := deltaSec } // [20.32] template (value) Require ts_Require(template (value) OptionTag_List optionsTags := {}) := { fieldName := REQUIRE_E, optionsTags := optionsTags } template (present) Require tr_Require(template (present) OptionTag_List optionsTags := ?) := { fieldName := REQUIRE_E, optionsTags := optionsTags } // [20.35 RFC2616 14.38] template (value) Server ts_Server(template (value) ServerVal_List serverBody := {}) := { fieldName := SERVER_E, serverBody := serverBody } template (present) Server tr_Server(template (present) ServerVal_List serverBody := ?) := { fieldName := SERVER_E, serverBody := serverBody } // [20.37] template (value) Supported ts_Supported(template (value) OptionTag_List optionsTags := {}) := { fieldName := SUPPORTED_E, optionsTags := optionsTags } template (present) Supported tr_Supported(template (present) OptionTag_List optionsTags := ?) := { fieldName := SUPPORTED_E, optionsTags := optionsTags } // [20.41 RFC2616 14.43] template (value) UserAgent ts_UserAgent(template (value) ServerVal_List userAgentBody := {}) := { fieldName := USER_AGENT_E, userAgentBody := userAgentBody } template (present) UserAgent tr_UserAgent(template (present) ServerVal_List userAgentBody := ?) := { fieldName := USER_AGENT_E, userAgentBody := userAgentBody } template (value) SipAddr ts_SipAddr(template (value) HostPort host_port, template (omit) UserInfo user_info := omit, template (omit) charstring displayName := omit, template (omit) SemicolonParam_List params := omit) := { addr := { nameAddr := { displayName := displayName, addrSpec := ts_SipUrl(host_port, user_info) } }, params := params } template (present) SipAddr tr_SipAddr(template (present) HostPort host_port := ?, template UserInfo user_info := *, template charstring displayName := *, template SemicolonParam_List params := *) := { addr := { nameAddr := { displayName := displayName, addrSpec := tr_SipUrl(host_port, user_info) } }, params := params } /* build a receive template from a value: substitute '*' for omit */ function tr_SipUrl_from_val(template (value) SipUrl tin) return template (present) SipUrl { var template (present) SipUrl ret := tin; /* if the port number is 5060, it may be omitted */ if (ispresent(tin.hostPort.portField) and valueof(tin.hostPort.portField) == 5060) { ret.hostPort.portField := 5060 ifpresent; } if (not ispresent(tin.userInfo.password)) { ret.userInfo.password := *; } return ret; } function tr_SipAddr_from_val(template (value) SipAddr tin) return template (present) SipAddr { var template (present) SipAddr ret := tin; if (not ispresent(tin.addr.nameAddr.displayName)) { ret.addr.nameAddr.displayName := *; } else if (f_str_tolower(f_sip_str_unquote(tin.addr.nameAddr.displayName)) == "anonymous") { /* if the user is Anonymous, it may be omitted */ ret.addr.nameAddr.displayName := tin.addr.nameAddr.displayName ifpresent; } ret.addr.nameAddr.addrSpec := tr_SipUrl_from_val(tin.addr.nameAddr.addrSpec); if (not ispresent(tin.params)) { ret.params := *; } return ret; } function ts_SipAddr_from_Addr_Union(template (value) Addr_Union au, template (omit) SemicolonParam_List params := omit) return template (value) SipAddr { var template (value) SipUrl addrSpec := ts_SipUrl_from_Addr_Union(au); var template (omit) charstring displayName; if (ischosen(au.nameAddr)) { displayName := au.nameAddr.displayName; } else { /* au.addrSpecUnion */ displayName := omit } return ts_SipAddr(addrSpec.hostPort, addrSpec.userInfo, displayName, params); } template (value) HostPort ts_HostPort(template (omit) charstring host := omit, template (omit) integer portField := omit) := { host := host, portField := portField } template (present) HostPort tr_HostPort(template charstring host := *, template integer portField := *) := { host := host, portField := portField } function f_tr_HostPort(template charstring host := *, template integer portField := *) return template (present) HostPort { return f_tr_HostPort_opt_defport(tr_HostPort(host, portField)); } function f_tr_HostPort_opt_defport(template (present) HostPort hp) return template (present) HostPort { var template (present) HostPort hpout := hp; /* if the port number is 5060, it may be omitted */ if (isvalue(hp.portField) and valueof(hp.portField) == 5060) { hpout.portField := 5060 ifpresent; } return hpout; } function f_tr_SipUrl_opt_defport(template (present) SipUrl url) return template (present) SipUrl { var template (present) SipUrl urlout := url; urlout.hostPort := f_tr_HostPort_opt_defport(url.hostPort); return urlout; } template (value) UserInfo ts_UserInfo(template (value) charstring userOrTelephoneSubscriber, template (omit) charstring password := omit) := { userOrTelephoneSubscriber := userOrTelephoneSubscriber, password := password } template (present) UserInfo tr_UserInfo(template (present) charstring userOrTelephoneSubscriber := ?, template charstring password := *) := { userOrTelephoneSubscriber := userOrTelephoneSubscriber, password := password } template (value) RequestLine ts_SIP_ReqLine(Method method, template (value) SipUrl uri, charstring ver := c_SIP_VERSION) := { method := method, requestUri := uri, sipVersion := ver } template (present) RequestLine tr_SIP_ReqLine(template (present) Method method := ?, template (present) SipUrl uri := ?, template (present) charstring ver := c_SIP_VERSION) := { method := method, requestUri := uri, sipVersion := ver } template (value) StatusLine ts_SIP_StatusLine(integer status_code, charstring reason) := { sipVersion := "SIP/2.0", statusCode := status_code, reasonPhrase := reason } template (present) StatusLine tr_SIP_StatusLine(template integer status_code, template charstring reason) := { sipVersion := "SIP/2.0", statusCode := status_code, reasonPhrase := reason } template (value) PDU_SIP_Request ts_SIP_req(template (value) RequestLine rl) := { requestLine := rl, msgHeader := c_SIP_msgHeader_empty, messageBody := omit, payload := omit } const Method_List c_SIP_defaultMethods := { "INVITE", "ACK", "BYE", "CANCEL", "OPTIONS", "PRACK", "MESSAGE", "SUBSCRIBE", "NOTIFY", "REFER", "UPDATE" }; private function f_ContentTypeOrOmit(template (omit) ContentType ct, template (omit) charstring body) return template (omit) ContentType { /* if user explicitly stated no content type */ if (istemplatekind(ct, "omit")) { return omit; } /* if there's no body, then there's no content-type either */ if (istemplatekind(body, "omit")) { return omit; } return ct; } private function f_ContentLength(template (omit) charstring body) return template (value) ContentLength { /* rfc3261 20.14: "If no body is present in a message, then the * Content-Length header field value MUST be set to zero." */ if (istemplatekind(body, "omit")) { return ts_ContentLength(0); } return ts_ContentLength(lengthof(body)); } template (value) ContentType ts_CT_SDP := { fieldName := CONTENT_TYPE_E, mediaType := "application/sdp" }; template (value) Via ts_Via_from(template (value) HostPort addr, template (value) charstring transport := "UDP") := { fieldName := VIA_E, viaBody := { { sentProtocol := { "SIP", "2.0", transport }, sentBy := addr, viaParams := omit } } } template (present) Via tr_Via_from(template (present) HostPort host_port := ?, template (present) charstring transport := ?, template SemicolonParam_List viaParams := *) := { fieldName := VIA_E, viaBody := { { sentProtocol := { "SIP", "2.0", ? }, sentBy := host_port, viaParams := viaParams } } } template (present) OtherAuth tr_OtherAuth(template (present) charstring authScheme := ?, template (present) CommaParam_List authParams := ?) := { authScheme := authScheme, authParams := authParams } template (value) OtherAuth ts_OtherAuth(template (value) charstring authScheme, template (value) CommaParam_List authParams) := { authScheme := authScheme, authParams := authParams } template (present) Challenge tr_Challenge_digestCln(template (present) CommaParam_List digestCln := ?) := { digestCln := digestCln } template (value) Challenge ts_Challenge_digestCln(template (value) CommaParam_List digestCln) := { digestCln := digestCln } template (present) Challenge tr_Challenge_otherChallenge(template (present) OtherAuth otherChallenge := ?) := { otherChallenge := otherChallenge } template (value) Challenge ts_Challenge_otherChallenge(template (value) OtherAuth otherChallenge) := { otherChallenge := otherChallenge } template (present) WwwAuthenticate tr_WwwAuthenticate(template (present) Challenge_list challenge := ?) := { fieldName := WWW_AUTHENTICATE_E, challenge := challenge } template (value) WwwAuthenticate ts_WwwAuthenticate(template (value) Challenge_list challenge) := { fieldName := WWW_AUTHENTICATE_E, challenge := challenge } // RFC3329 template (present) Security_client tr_Security_client(template (present) Security_mechanism_list sec_mechanism_list := ?) := { fieldName := SECURITY_CLIENT_E, sec_mechanism_list := sec_mechanism_list } template (value) Security_client ts_Security_client(template (value) Security_mechanism_list sec_mechanism_list) := { fieldName := SECURITY_CLIENT_E, sec_mechanism_list := sec_mechanism_list } template (present) Security_server tr_Security_server(template (present) Security_mechanism_list sec_mechanism_list := ?) := { fieldName := SECURITY_SERVER_E, sec_mechanism_list := sec_mechanism_list } template (value) Security_server ts_Security_server(template (value) Security_mechanism_list sec_mechanism_list) := { fieldName := SECURITY_SERVER_E, sec_mechanism_list := sec_mechanism_list } template (present) Security_mechanism tr_Security_mechanism(template (present) charstring name := ?, template SemicolonParam_List params := *) := { mechanism_name := name, mechanism_params := params } template (value) Security_mechanism ts_Security_mechanism(template (value) charstring name, template (omit) SemicolonParam_List params := omit) := { mechanism_name := name, mechanism_params := params } template (value) MessageHeader ts_SIP_msgHeader_empty := c_SIP_msgHeader_empty; template (value) MessageHeader ts_SIP_msgh_std(template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, template (omit) Contact contact, template (value) charstring method, template (value) integer seq_nr, template (value) Via via, template (omit) ContentLength content_length := ts_ContentLength(0), template (omit) ContentType content_type := omit, template (omit)Authorization authorization := omit, template (omit) Allow allow := ts_Allow(c_SIP_defaultMethods), template (omit) Expires expires := omit, template (omit) Require require := omit, template (omit) Security_client security_client := omit, template (omit) Security_server security_server := omit, template (omit) Server server := omit, template (omit) Supported supported := omit, template (omit) UserAgent userAgent := ts_UserAgent({ "osmo-ttcn3-hacks/0.23" }), template (omit) WwwAuthenticate wwwAuthenticate := omit ) modifies ts_SIP_msgHeader_empty := { allow := allow, authorization := authorization, callId := { fieldName := CALL_ID_E, callid := call_id }, contact := contact, contentLength := content_length, contentType := content_type, cSeq := { fieldName := CSEQ_E, seqNumber := seq_nr, method := method }, expires := expires, fromField := { fieldName := FROM_E, addressField := from_addr.addr, fromParams := from_addr.params }, require := require, security_client := security_client, security_server := security_server, server := server, supported := supported, toField := { fieldName := TO_E, addressField := to_addr.addr, toParams := to_addr.params }, userAgent := userAgent, via := via, wwwAuthenticate := wwwAuthenticate } template (present) MessageHeader tr_SIP_msgh_std(template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template Contact contact, template (present) Via via := tr_Via_from(?), template charstring method, template integer seq_nr := ?, template ContentLength content_length := *, template ContentType content_type := *, template Allow allow := *, template Authorization authorization := *, template Expires expires := *, template Require require := *, template Security_client security_client := *, template Security_server security_server := *, template Server server := *, template Supported supported := *, template UserAgent userAgent := *, template WwwAuthenticate wwwAuthenticate := * ) modifies t_SIP_msgHeader_any := { allow := allow, authorization := authorization, callId := { fieldName := CALL_ID_E, callid := call_id }, contact := contact, contentLength := content_length, contentType := content_type, cSeq := { fieldName := CSEQ_E, seqNumber := seq_nr, method := method }, expires := expires, fromField := { fieldName := FROM_E, addressField := from_addr.addr, fromParams := from_addr.params }, require := require, security_client := security_client, security_server := security_server, server := server, supported := supported, toField := { fieldName := TO_E, addressField := to_addr.addr, toParams := to_addr.params }, userAgent := userAgent, via := via, wwwAuthenticate := wwwAuthenticate } template (value) PDU_SIP_Request ts_SIP_REGISTER(template (value) SipUrl sip_url_host_port, template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, template (value) Via via, integer seq_nr, template (omit) Contact contact, template (omit) Expires expires, template (omit) Authorization authorization := omit, template (omit) Require require := omit, template (omit) Security_client security_client := omit, template (omit) Supported supported := omit, template (omit) charstring body := omit) := { requestLine := ts_SIP_ReqLine(REGISTER_E, sip_url_host_port), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact, "REGISTER", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), authorization := authorization, expires := expires, require := require, security_client := security_client, supported := supported), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_REGISTER(template (present) SipUrl sip_url_host_port := ?, template (present) CallidString call_id := ?, template (present) SipAddr from_addr := ?, template (present) SipAddr to_addr := ?, template (present) Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)), template integer seq_nr := *, template Authorization authorization := *, template Contact contact := *, template Expires expires := *, template Require require := *, template Security_client security_client := *, template Supported supported := *, template charstring body := *) := { requestLine := tr_SIP_ReqLine(REGISTER_E, sip_url_host_port), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, via, "REGISTER", seq_nr, authorization := authorization, expires := expires, require := require, security_client := security_client, supported := supported), messageBody := body, payload := omit } template (value) PDU_SIP_Request ts_SIP_INVITE(template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, template (value) Via via, template (value) Contact contact, integer seq_nr, template (omit) charstring body := omit) := { requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addr.nameAddr.addrSpec), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact, "INVITE", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body)), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_INVITE(template (present) SipUrl uri, template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)), template integer seq_nr, template charstring body) := { requestLine := tr_SIP_ReqLine(INVITE_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, ?, via, "INVITE", seq_nr), messageBody := body, payload := omit } template (value) PDU_SIP_Request ts_SIP_BYE(CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, template (value) Via via, integer seq_nr, template (omit) charstring body) := { requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addr.nameAddr.addrSpec), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "BYE", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body)), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_BYE(template (present) SipUrl uri, template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template Via via, template integer seq_nr, template charstring body := *) := { requestLine := tr_SIP_ReqLine(BYE_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit, via, "BYE", seq_nr), messageBody := body, payload := omit } template (value) PDU_SIP_Request ts_SIP_ACK(template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, template (value) Via via, integer seq_nr, template (omit) charstring body) := { requestLine := ts_SIP_ReqLine(ACK_E, to_addr.addr.nameAddr.addrSpec), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, ts_Contact_SipAddr(from_addr), "ACK", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body)), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_ACK(template (present) SipUrl uri, template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template Via via, template integer seq_nr, template charstring body) := { requestLine := tr_SIP_ReqLine(ACK_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, "ACK", seq_nr), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_CANCEL(template (present) SipUrl uri, template (present) CallidString call_id, template (present) SipAddr from_addr, template (present) SipAddr to_addr, template (present) Via via, template (present) integer seq_nr, template charstring body := *) := { requestLine := tr_SIP_ReqLine(CANCEL_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, "CANCEL", seq_nr), messageBody := body, payload := omit } template (value) PDU_SIP_Response ts_SIP_Response(template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, charstring method, integer status_code, integer seq_nr, charstring reason, Via via, template (omit) Allow allow := omit, template (omit) Require require := omit, template (omit) Server server := omit, template (omit) Supported supported := omit, template (omit) UserAgent userAgent := omit, template (omit) charstring body := omit) := { statusLine := ts_SIP_StatusLine(status_code, reason), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), allow := allow, require := require, server := server, supported := supported, userAgent := userAgent), messageBody := body, payload := omit } /* 100 Trying */ template (value) PDU_SIP_Response ts_SIP_Response_Trying( template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, Via via, integer seq_nr, charstring method := "INVITE", template (omit) Allow allow := omit, template (omit) Server server := omit, template (omit) UserAgent userAgent := omit, template (omit) charstring body := omit) := { statusLine := ts_SIP_StatusLine(100, "Trying"), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), allow := allow, server := server, userAgent := userAgent), messageBody := body, payload := omit } /* 180 Ringing */ template (value) PDU_SIP_Response ts_SIP_Response_Ringing( template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, Via via, integer seq_nr, charstring method := "INVITE", template (omit) charstring body := omit) := { statusLine := ts_SIP_StatusLine(180, "Ringing"), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body)), messageBody := body, payload := omit } /* 401 Unauthorized */ template (value) PDU_SIP_Response ts_SIP_Response_Unauthorized( template (value) CallidString call_id, template (value) SipAddr from_addr, template (value) SipAddr to_addr, Via via, template (value) WwwAuthenticate wwwAuthenticate, integer seq_nr, charstring method := "REGISTER", template (omit) Allow allow := omit, template (omit) Security_server security_server := omit, template (omit) Server server := omit, template (omit) Supported supported := omit, template (omit) UserAgent userAgent := omit, template (omit) charstring body := omit) := { statusLine := ts_SIP_StatusLine(401, "Unauthorized"), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), allow := allow, security_server := security_server, server := server, supported := supported, userAgent := userAgent, wwwAuthenticate := wwwAuthenticate), messageBody := body, payload := omit } template (present) PDU_SIP_Response tr_SIP_Response(template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template (present) Via via := tr_Via_from(?), template Contact contact, template charstring method, template integer status_code, template integer seq_nr := ?, template charstring reason := ?, template charstring body := *) := { statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, via, method, seq_nr), messageBody := body, payload := omit } /* Expect during first REGISTER/INVITE/... when authorization is required: */ template (present) PDU_SIP_Response tr_SIP_Response_Unauthorized( template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template (present) Via via := tr_Via_from(?), template Contact contact := *, template (present) WwwAuthenticate wwwAuthenticate := ?, template integer seq_nr := ?, template charstring method := "REGISTER", template integer status_code := 401, template charstring reason := "Unauthorized", template charstring body := *) := { statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, via, method, seq_nr, wwwAuthenticate := wwwAuthenticate), messageBody := body, payload := omit } /* 100 Trying */ template (present) PDU_SIP_Response tr_SIP_Response_Trying( template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template (present) Via via := tr_Via_from(?), template integer seq_nr := ?, template charstring method := "INVITE", template integer status_code := 100, template charstring reason := "Trying", template charstring body := *) := { statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, omit, via, method, seq_nr), messageBody := body, payload := omit } /* 180 Ringing */ template (present) PDU_SIP_Response tr_SIP_Response_Ringing( template CallidString call_id, template SipAddr from_addr, template SipAddr to_addr, template (present) Via via := tr_Via_from(?), template integer seq_nr := ?, template charstring method := "INVITE", template integer status_code := 180, template charstring reason := "Ringing", template charstring body := *) := { statusLine := tr_SIP_StatusLine(status_code, reason), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, method, seq_nr), messageBody := body, payload := omit } /**************** * FUNCTIONS: ****************/ function f_sip_param_find(GenericParam_List li, template (present) charstring id := ?) return template (omit) GenericParam { var integer i; for (i := 0; i < lengthof(li); i := i + 1) { if (not ispresent(li[i])) { continue; } if (match(li[i].id, id)) { return li[i]; } } return omit; } function f_sip_param_find_or_fail(GenericParam_List li, template (present) charstring id := ?) return GenericParam { var template (omit) GenericParam parameter; parameter := f_sip_param_find(li, id); if (istemplatekind(parameter, "omit")) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Param ", id, " not found in ", li)); } return valueof(parameter); } function f_sip_param_get_value(GenericParam_List li, template (present) charstring id := ?) return template (omit) charstring { var template (omit) GenericParam parameter; parameter := f_sip_param_find(li, id); if (istemplatekind(parameter, "omit")) { return omit; } return parameter.paramValue; } function f_sip_param_get_value_or_fail(GenericParam_List li, template (present) charstring id := ?) return template (omit) charstring { var GenericParam parameter; parameter := f_sip_param_find_or_fail(li, id); return parameter.paramValue; } function f_sip_param_get_value_present_or_fail(GenericParam_List li, template (present) charstring id := ?) return charstring { var GenericParam parameter; parameter := f_sip_param_find_or_fail(li, id); if (not ispresent(parameter.paramValue)) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Param ", id, " value not present in ", li)); } return parameter.paramValue; } function f_sip_param_match_value(GenericParam_List li, template (present) charstring id := ?, template charstring exp_paramValue := *) return boolean { var template (omit) charstring val; val := f_sip_param_get_value_or_fail(li, id); if (istemplatekind(val, "omit")) { return istemplatekind(val, "omit") or istemplatekind(val, "*"); } return match(valueof(val), exp_paramValue); } function f_sip_param_match_value_or_fail(GenericParam_List li, template (present) charstring id := ?, template charstring exp_paramValue := *) { var template (omit) charstring val := f_sip_param_get_value_or_fail(li, id); if (istemplatekind(val, "omit")) { if (istemplatekind(val, "omit") or istemplatekind(val, "*")) { return; } else { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Param ", id, " match failed: val ", val, " vs exp ", exp_paramValue)); } } if (not match(valueof(val), exp_paramValue)) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Param ", id, " match failed: val ", val, " vs exp ", exp_paramValue)); } } function f_sip_param_remove(template (omit) GenericParam_List li_tpl, charstring id) return GenericParam_List { var integer i; var GenericParam_List li; var GenericParam_List new_li := {}; if (istemplatekind(li_tpl, "omit")) { return {}; } li := valueof(li_tpl); for (i := 0; i < lengthof(li); i := i + 1) { if (not ispresent(li[i]) or not match(li[i].id, id)) { new_li := new_li & {li[i]}; } } return new_li; } function f_sip_param_set(template (omit) GenericParam_List li_tpl, charstring id, charstring val) return GenericParam_List { var integer i; var GenericParam_List li; var GenericParam_List new_li := {}; var boolean found := false; if (istemplatekind(li_tpl, "omit")) { return { valueof(ts_Param(id, val)) }; } li := valueof(li_tpl); for (i := 0; i < lengthof(li); i := i + 1) { if (not ispresent(li[i]) or not match(li[i].id, id)) { new_li := new_li & {li[i]}; continue; } new_li := new_li & { valueof(ts_Param(li[i].id, val)) }; found := true; } if (not found) { new_li := new_li & { valueof(ts_Param(id, val)) }; } return new_li; } /* Make sure string is quoted. */ function f_sip_str_quote(template (value) charstring val) return charstring { var charstring str := valueof(val); if (lengthof(str) == 0) { return ""; } if (str[0] != "\"") { return "\"" & str & "\""; } return str; } /* Make sure string is unquoted. * Similar to unq() in RFC 2617 */ function f_sip_str_unquote(template (value) charstring val) return charstring { var charstring str := valueof(val); var integer len := lengthof(str); if (len <= 1) { return str; } if (str[0] == "\"" and str[len - 1] == "\"") { return substr(str, 1, len - 2); } return str; } /* RFC 2617 3.2.2.2 A1 */ function f_sip_digest_A1(charstring user, charstring realm, charstring password) return charstring { /* RFC 2617 3.2.2.2 A1 */ var charstring A1 := f_sip_str_unquote(user) & ":" & f_sip_str_unquote(realm) & ":" & password; var charstring digestA1 := f_str_tolower(f_calculateMD5(A1)); log("A1: md5('", A1, "') = ", digestA1); return digestA1; } /* RFC 2617 3.2.2.2 A2 */ function f_sip_digest_A2(charstring method, charstring uri) return charstring { var charstring A2 := method & ":" & uri var charstring digestA2 := f_str_tolower(f_calculateMD5(A2)); log("A2: md5('", A2, "') = ", digestA2); return digestA2; } /* RFC 2617 3.2.2.1 Request-Digest */ function f_sip_digest_RequestDigest(charstring digestA1, charstring nonce, charstring nc, charstring cnonce, charstring qop, charstring digestA2) return charstring { var charstring digest_data := f_sip_str_unquote(nonce) & ":" & nc & ":" & cnonce & ":" & f_sip_str_unquote(qop) & ":" & digestA2; var charstring req_digest := f_sip_digest_KD(digestA1, digest_data); log("Request-Digest: md5('", digestA1, ":", digest_data ,"') = ", req_digest); return req_digest; } /* RFC 2617 3.2.1 The WWW-Authenticate Response Header * KD(secret, data) = H(concat(secret, ":", data)) */ function f_sip_digest_KD(charstring secret, charstring data) return charstring { return f_str_tolower(f_calculateMD5(secret & ":" & data)); } /* Digest Auth: RFC 2617 */ function f_sip_digest_gen_Authorization(WwwAuthenticate www_authenticate, charstring user, charstring password, charstring method, charstring uri, charstring cnonce := "0a4f113b", integer nc_int := 1) return Authorization { var CommaParam_List digestCln; var template (value) Authorization authorization; var template (value) Credentials cred; var template (omit) GenericParam rx_param; digestCln := www_authenticate.challenge[0].digestCln; var charstring algorithm; rx_param := f_sip_param_find(digestCln, "algorithm"); if (istemplatekind(rx_param, "omit")) { /* Assume MD5 if not set */ algorithm := "MD5" } else { algorithm := valueof(rx_param.paramValue); if (f_strstr(algorithm, "MD5") == -1) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected algorithm: ", algorithm)); } } var charstring realm := f_sip_param_get_value_present_or_fail(digestCln, "realm"); var charstring nonce := f_sip_param_get_value_present_or_fail(digestCln, "nonce"); var charstring opaque := f_sip_param_get_value_present_or_fail(digestCln, "opaque"); var charstring qop := f_sip_param_get_value_present_or_fail(digestCln, "qop"); if (f_strstr(qop, "auth") == -1) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop)); } var charstring selected_qop := "auth"; /* RFC 2617 3.2.2.2 A1 */ var charstring digestA1 := f_sip_digest_A1(user, realm, password); /* RFC 2617 3.2.2.3 A2 */ var charstring digestA2 := f_sip_digest_A2(method, uri); /* RFC 2617 3.2.2.1 Request-Digest */ var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8))); var charstring req_digest := f_sip_digest_RequestDigest(digestA1, nonce, nc, cnonce, selected_qop, digestA2); cred := ts_Credentials_DigestResponseMD5(user, realm, nonce, uri, req_digest, opaque, algorithm, selected_qop, cnonce, nc); authorization := ts_Authorization(cred); return valueof(authorization); } /* RFC 2617 3.5 Example */ function f_sip_digest_selftest() { /* The following example assumes that an access-protected document is being requested from the server via a GET request. The URI of the document is "http://www.nowhere.org/dir/index.html". Both client and server know that the username for this document is "Mufasa", and the password is "Circle Of Life" (with one space between each of the three words). HTTP/1.1 401 Unauthorized WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" Authorization: Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41" */ var template (value) CommaParam_List digestCln := { ts_Param("realm", f_sip_str_quote("testrealm@host.com")), ts_Param("qop", f_sip_str_quote("auth,auth-int")), ts_Param("nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")), ts_Param("opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41")) }; var template (value) WwwAuthenticate www_authenticate := ts_WwwAuthenticate( { ts_Challenge_digestCln(digestCln) } ) var Authorization authorization := f_sip_digest_gen_Authorization(valueof(www_authenticate), "Mufasa", "Circle Of Life", "GET", "/dir/index.html", cnonce := "0a4f113b", nc_int := 1); var CommaParam_List digestResp := authorization.body.digestResponse; f_sip_param_match_value_or_fail(digestResp, "realm", f_sip_str_quote("testrealm@host.com")); f_sip_param_match_value_or_fail(digestResp, "nonce", f_sip_str_quote("dcd98b7102dd2f0e8b11d0f600bfb0c093")); f_sip_param_match_value_or_fail(digestResp, "uri", f_sip_str_quote("/dir/index.html")); f_sip_param_match_value_or_fail(digestResp, "qop", "auth"); f_sip_param_match_value_or_fail(digestResp, "nc", "00000001"); f_sip_param_match_value_or_fail(digestResp, "cnonce", f_sip_str_quote("0a4f113b")); f_sip_param_match_value_or_fail(digestResp, "response", f_sip_str_quote("6629fae49393a05397450978507c4ef1")); f_sip_param_match_value_or_fail(digestResp, "opaque", f_sip_str_quote("5ccc069c403ebaf9f0171e9517f40e41")); } /* RFC 3261 8.1.1.5: * "The sequence number value MUST be expressible as a 32-bit unsigned integer * and MUST be less than 2**31." */ function f_sip_rand_seq_nr() return integer { /* 2**31 = 2147483648 */ return f_rnd_int(2147483648) } function f_sip_next_seq_nr(integer seq_nr) return integer { return (seq_nr + 1) mod 2147483648; } function f_sip_Request_inc_seq_nr(inout template (value) PDU_SIP_Request req) { req.msgHeader.cSeq.seqNumber := f_sip_next_seq_nr(valueof(req.msgHeader.cSeq.seqNumber)); } function f_sip_rand_tag() return charstring { /* Tags shall have at least 32 bit of randomness */ var integer rnd_int := f_rnd_int(4294967296); /* Make collisions harder by appending time to the final string: */ var integer ts_int := f_time_ms() mod 4294967296; return hex2str(int2hex(rnd_int, 8)) & "-" & hex2str(int2hex(ts_int, 8)); } /* Generate a "branch" tag value. * RFC 3261 p.105 section 8: * "A common way to create this value is to compute a * cryptographic hash of the To tag, From tag, Call-ID header * field, the Request-URI of the request received (before * translation), the topmost Via header, and the sequence number * from the CSeq header field, in addition to any Proxy-Require * and Proxy-Authorization header fields that may be present. The * algorithm used to compute the hash is implementation-dependent, * but MD5 (RFC 1321 [35]),expressed in hexadecimal, is a reasonable * choice." * See also Section 8.1.1.7: * "The branch ID inserted by an element compliant with this * specification MUST always begin with the characters "z9hG4bK"." */ const charstring sip_magic_cookie := "z9hG4bK"; function f_sip_gen_branch(charstring tag_to, charstring tag_from, charstring tag_call_id, integer cseq) return charstring { var charstring str := tag_to & tag_from & tag_call_id & int2str(cseq); var charstring hash := f_calculateMD5(str); var charstring branch := sip_magic_cookie & hash; return branch; } function f_sip_HostPort_to_str(HostPort host_port) return charstring { var charstring str := ""; if (ispresent(host_port.host)) { str := host_port.host; } if (ispresent(host_port.portField)) { str := str & ":" & int2str(host_port.portField); } return str; } function f_sip_SipUrl_to_str(SipUrl uri) return charstring { var charstring str := uri.scheme & ":" & f_sip_HostPort_to_str(uri.hostPort); return str; } function f_sip_NameAddr_to_str(NameAddr naddr) return charstring { if (ispresent(naddr.displayName)) { return naddr.displayName & " <" & f_sip_SipUrl_to_str(naddr.addrSpec) & ">"; } else { return f_sip_SipUrl_to_str(naddr.addrSpec); } } function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring { if (ischosen(sip_addr.addr.nameAddr)) { return f_sip_NameAddr_to_str(sip_addr.addr.nameAddr); } else { return f_sip_SipUrl_to_str(sip_addr.addr.addrSpecUnion); } } }