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, template (value) charstring scheme := "sip") := { scheme := scheme, userInfo := user_info, hostPort := host_port, urlParameters := omit, headers := omit } template (present) SipUrl tr_SipUrl(template (present) HostPort host_port := ?, template UserInfo user_info := *, template (present) charstring scheme := "sip") := { scheme := scheme, userInfo := user_info, hostPort := host_port, urlParameters := *, headers := * } template (value) SipUrl ts_SipUrlHost(template (value) charstring host, template (omit) integer portField := omit, template (value) charstring scheme := "sip") := ts_SipUrl(ts_HostPort(host, portField), scheme := scheme); 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.1, RFC2616 14.1] template (present) AcceptBody tr_AcceptBody(template (present) charstring mediaRange, template SemicolonParam_List acceptParam := *) := { mediaRange := mediaRange, acceptParam := acceptParam } template (value) AcceptBody ts_AcceptBody(template (value) charstring mediaRange, template (omit) SemicolonParam_List acceptParam := omit) := { mediaRange := mediaRange, acceptParam := acceptParam } template (present) Accept tr_Accept(template AcceptBody_List acceptArgs := *) := { fieldName := ACCEPT_E, acceptArgs := acceptArgs } template (value) Accept ts_Accept(template (omit) AcceptBody_List acceptArgs := omit) := { fieldName := ACCEPT_E, acceptArgs := acceptArgs } // [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 } template (present) Expires tr_Expires(template (present) DeltaSec deltaSec := ?) := { fieldName := EXPIRES_E, deltaSec := deltaSec } // [20.20] template (value) From ts_From(template (value) Addr_Union addressField, template (omit) SemicolonParam_List fromParams := omit) := { fieldName := FROM_E, addressField := addressField, fromParams := fromParams } template (present) From tr_From(template (present) Addr_Union addressField := ?, template SemicolonParam_List fromParams := *) := { fieldName := FROM_E, addressField := addressField, fromParams := fromParams } // [20.23] template (value) MinExpires ts_MinExpires(template (value) DeltaSec deltaSec := "800000") := { fieldName := MIN_EXPIRES_E, deltaSec := deltaSec } // [RFC draft-ietf-sip-session-timer-15] template (value) Min_SE ts_Min_SE(template (value) DeltaSec deltaSec := "800000", template (omit) SemicolonParam_List params := omit) := { fieldName := MIN_SE_E, deltaSec := deltaSec, params := params } template (present) Min_SE tr_Min_SE(template (present) DeltaSec deltaSec := ?, template SemicolonParam_List params := *) := { fieldName := MIN_SE_E, deltaSec := deltaSec, params := params } // [RFC3455 5.4] + 3GPP 24.229 V8.7.0 template (present) Access_net_spec tr_Access_net_spec(template (present) charstring access_type := ?, template SemicolonParam_List access_info := *) := { access_type := access_type, access_info := access_info } template (present) Access_net_spec tr_Access_net_spec_EUTRAN(template (present) charstring uli_str := ?) := { access_type := "3GPP-E-UTRAN-FDD", access_info := {tr_Param("utran-cell-id-3gpp", uli_str)} } // [RFC3455 5.4] + 3GPP 24.229 V8.7.0 template (present) P_Access_Network_Info tr_P_Access_Network_Info(template (present) Access_net_spec_list access_net_specs := ?) := { fieldName := P_ACCESS_NETWORK_INFO, access_net_specs := access_net_specs } // [RFC3455 4.1] template (present) P_Assoc_uri_spec tr_P_Assoc_uri_spec(template (present) NameAddr p_asso_uri := ?, template SemicolonParam_List ai_params := *) := { p_asso_uri := p_asso_uri, ai_params := ai_params } template (value) P_Assoc_uri_spec ts_P_Assoc_uri_spec(template (value) NameAddr p_asso_uri, template (omit) SemicolonParam_List ai_params := omit) := { p_asso_uri := p_asso_uri, ai_params := ai_params } // [RFC3455 4.1] template (present) P_Associated_Uri tr_P_Associated_Uri(template (present) P_Assoc_uri_spec_list p_assoc_uris := ?) := { fieldName := P_ASSOCIATED_URI, p_assoc_uris := p_assoc_uris } template (value) P_Associated_Uri ts_P_Associated_Uri(template (value) P_Assoc_uri_spec_list p_assoc_uris := {}) := { fieldName := P_ASSOCIATED_URI, p_assoc_uris := p_assoc_uris } // RFC5009 template (present) P_Early_Media tr_P_Early_Media(template Em_param_List em_param_list := *) := { fieldName := P_EARLY_MEDIA_E, em_param_list := em_param_list } template (value) P_Early_Media ts_P_Early_Media(template (omit) Em_param_List em_param_list := omit) := { fieldName := P_EARLY_MEDIA_E, em_param_list := em_param_list } // [RFC6050] template (present) P_Preferred_Service tr_P_Preferred_Service(template (present) Service_ID_List p_ps := ?) := { fieldName := P_PREFERRED_SERVICE_E, p_ps := p_ps } // RFC 3262 template (value) RAck ts_RAck(template (value) integer response_num, template (value) integer seq_nr, template (value) charstring method := "INVITE") := { fieldName := RACK_E, response_num := response_num, seqNumber := seq_nr, method := method } template (present) RAck tr_RAck(template (present) integer response_num := ?, template (present) integer seq_nr := ?, template (present) charstring method := ?) := { fieldName := RACK_E, response_num := response_num, seqNumber := seq_nr, method := method } // [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 } // RFC 3262 template (value) RSeq ts_RSeq(template (value) integer response_num) := { fieldName := RSEQ_E, response_num := response_num } template (present) RSeq tr_RSeq(template (present) integer response_num := ?) := { fieldName := RSEQ_E, response_num := response_num } // [RFC draft-ietf-sip-session-timer-15] template (value) Session_expires ts_Session_expires(template (value) DeltaSec deltaSec, template (omit) SemicolonParam_List se_params := omit) := { fieldName := SESSION_EXPIRES_E, deltaSec := deltaSec, se_params := se_params } // [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.39] template (value) To ts_To(template (value) Addr_Union addressField, template (omit) SemicolonParam_List toParams := omit) := { fieldName := TO_E, addressField := addressField, toParams := toParams } template (present) To tr_To(template (present) Addr_Union addressField := ?, template SemicolonParam_List toParams := *) := { fieldName := TO_E, addressField := addressField, toParams := toParams } // [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_Addr_Union_from_val(template (value) Addr_Union tin) return template (present) Addr_Union { var template (present) Addr_Union ret := tin; if (not ispresent(tin.nameAddr.displayName)) { ret.nameAddr.displayName := *; } else if (f_str_tolower(f_sip_str_unquote(tin.nameAddr.displayName)) == "anonymous") { /* if the user is Anonymous, it may be omitted */ ret.nameAddr.displayName := tin.nameAddr.displayName ifpresent; } ret.nameAddr.addrSpec := tr_SipUrl_from_val(tin.nameAddr.addrSpec); return ret; } function tr_SipAddr_from_val(template (value) SipAddr tin) return template (present) SipAddr { var template (present) SipAddr ret := tin; ret.addr := tr_Addr_Union_from_val(tin.addr); 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) From from_addr, template (value) To 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) Accept accept := omit, template (omit) Authorization authorization := omit, template (omit) Allow allow := ts_Allow(c_SIP_defaultMethods), template (omit) Expires expires := omit, template (omit) MinExpires minExpires := omit, template (omit) Min_SE min_SE := omit, template (omit) P_Associated_Uri p_associated_uri := omit, template (omit) P_Early_Media p_early_media := omit, template (omit) RAck rack := omit, template (omit) Require require := omit, template (omit) RSeq rseq := omit, template (omit) Security_client security_client := omit, template (omit) Security_server security_server := omit, template (omit) Server server := omit, template (omit) Session_expires session_expires := 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 := { accept := accept, 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 := from_addr, minExpires := minExpires, min_SE := min_SE, p_associated_uri := p_associated_uri, p_Early_Media := p_early_media, rack := rack, require := require, rseq := rseq, security_client := security_client, security_server := security_server, server := server, session_expires := session_expires, supported := supported, toField := to_addr, userAgent := userAgent, via := via, wwwAuthenticate := wwwAuthenticate } template (present) MessageHeader tr_SIP_msgh_std(template CallidString call_id, template From from_addr, template To 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 Accept accept := *, template Allow allow := *, template Authorization authorization := *, template Expires expires := *, template Min_SE min_SE := *, template P_Associated_Uri p_associated_uri := *, template P_Early_Media p_early_media := *, template RAck rack := *, template Require require := *, template RSeq rseq := *, template Security_client security_client := *, template Security_server security_server := *, template Session_expires session_expires := *, template Server server := *, template Supported supported := *, template UserAgent userAgent := *, template WwwAuthenticate wwwAuthenticate := * ) modifies t_SIP_msgHeader_any := { accept := accept, 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 := from_addr, min_SE := min_SE, p_associated_uri := p_associated_uri, p_Early_Media := p_early_media, rack := rack, require := require, rseq := rseq, security_client := security_client, security_server := security_server, session_expires := session_expires, server := server, supported := supported, toField := to_addr, 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) From from_addr, template (value) To 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) From from_addr := ?, template (present) To 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) From from_addr, template (value) To to_addr, template (value) Via via, template (value) Contact contact, integer seq_nr, template (omit) Accept accept := omit, template (omit) P_Early_Media p_early_media := omit, template (omit) Min_SE min_SE := omit, template (omit) Session_expires session_expires := omit, template (omit) Supported supported := omit, template (omit) charstring body := omit) := { requestLine := ts_SIP_ReqLine(INVITE_E, to_addr.addressField.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), accept := accept, min_SE := min_SE, p_early_media := p_early_media, session_expires := session_expires, supported := supported), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_INVITE(template (present) SipUrl uri, template CallidString call_id, template From from_addr, template To to_addr, template Via via := tr_Via_from(f_tr_HostPort_opt_defport(?)), template integer seq_nr, template Accept accept := *, template P_Early_Media p_early_media := *, template Min_SE min_SE := *, template Session_expires session_expires := *, template Supported supported := *, 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, accept := accept, min_SE := min_SE, p_early_media := p_early_media, session_expires := session_expires), messageBody := body, payload := omit } template (value) PDU_SIP_Request ts_SIP_BYE(CallidString call_id, template (value) From from_addr, template (value) To to_addr, template (value) Via via, integer seq_nr, template (omit) charstring body) := { requestLine := ts_SIP_ReqLine(BYE_E, to_addr.addressField.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 From from_addr, template To 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) SipUrl uri, template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, template (value) Via via, integer seq_nr, template (omit) charstring body) := { requestLine := ts_SIP_ReqLine(ACK_E, uri), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, ts_Contact({ ts_ContactAddress(from_addr.addressField, from_addr.fromParams) }), "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 From from_addr, template To 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) From from_addr, template (present) To 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_Request ts_SIP_PRACK(template (value) SipUrl uri, template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, template (value) Via via, integer seq_nr, template (value) RAck rack, template (omit) charstring body := omit) := { requestLine := ts_SIP_ReqLine(PRACK_E, uri), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, "PRACK", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), rack := rack), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_PRACK(template (present) SipUrl uri, template CallidString call_id, template From from_addr, template To to_addr, template Via via, template integer seq_nr, template RAck rack := tr_RAck(?, ?, ?), template charstring body := *) := { requestLine := tr_SIP_ReqLine(PRACK_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, "PRACK", seq_nr, rack := rack), messageBody := body, payload := omit } template (value) PDU_SIP_Request ts_SIP_UPDATE(template (value) SipUrl uri, template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, template (value) Via via, integer seq_nr, template (omit) Contact contact, template (value) Require require := ts_Require({"sec-agree", "precondition"}), template (value) charstring body) := { requestLine := ts_SIP_ReqLine(UPDATE_E, uri), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, contact, "UPDATE", seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), require := require), messageBody := body, payload := omit } template (present) PDU_SIP_Request tr_SIP_UPDATE(template (present) SipUrl uri, template CallidString call_id, template From from_addr, template To to_addr, template Via via, template integer seq_nr, template Require require := *, template Supported supported := *, template charstring body) := { requestLine := tr_SIP_ReqLine(UPDATE_E, uri), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, "UPDATE", seq_nr, require := require, supported := supported), messageBody := body, payload := omit } template (value) PDU_SIP_Response ts_SIP_Response(template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, charstring method, integer status_code, integer seq_nr, charstring reason, Via via, template (omit) Allow allow := omit, template (omit) Contact contact := omit, template (omit) P_Associated_Uri p_associated_uri := omit, template (omit) Require require := omit, template (omit) Server server := omit, template (omit) Session_expires session_expires := 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, contact, method, seq_nr, via, content_length := f_ContentLength(body), content_type := f_ContentTypeOrOmit(ts_CT_SDP, body), allow := allow, p_associated_uri := p_associated_uri, require := require, server := server, session_expires := session_expires, 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) From from_addr, template (value) To 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) From from_addr, template (value) To 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 } /* 183 Session Progress */ template (value) PDU_SIP_Response ts_SIP_Response_SessionProgress( template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, Via via, integer seq_nr, charstring method := "INVITE", template (omit) P_Early_Media p_early_media := omit, template (omit) Require require := ts_Require({"100rel", "precondition"}), template (omit) RSeq rseq := ts_RSeq(1), template (omit) charstring body := omit) := { statusLine := ts_SIP_StatusLine(183, "Session Progress"), 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), p_early_media := p_early_media, require := require, rseq := rseq), messageBody := body, payload := omit } /* 401 Unauthorized */ template (value) PDU_SIP_Response ts_SIP_Response_Unauthorized( template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, Via via, template (value) WwwAuthenticate wwwAuthenticate, integer seq_nr, charstring method := "REGISTER", template (omit) Allow allow := omit, template (omit) P_Associated_Uri p_associated_uri := 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, p_associated_uri := p_associated_uri, security_server := security_server, server := server, supported := supported, userAgent := userAgent, wwwAuthenticate := wwwAuthenticate), messageBody := body, payload := omit } /* Tx 423 Interval Too Brief */ template (value) PDU_SIP_Response ts_SIP_Response_423_Interval_Too_Brief( template (value) CallidString call_id, template (value) From from_addr, template (value) To to_addr, Via via, integer seq_nr, charstring method := "REGISTER", template (omit) Allow allow := omit, template (value) MinExpires minExpires := ts_MinExpires(), template (omit) Server server := omit, template (omit) Supported supported := omit, template (omit) UserAgent userAgent := omit) := { statusLine := ts_SIP_StatusLine(423, "Interval Too Brief"), msgHeader := ts_SIP_msgh_std(call_id, from_addr, to_addr, omit, method, seq_nr, via, content_length := f_ContentLength(omit), content_type := f_ContentTypeOrOmit(ts_CT_SDP, omit), allow := allow, minExpires := minExpires, server := server, supported := supported, userAgent := userAgent), messageBody := omit, payload := omit } template (present) PDU_SIP_Response tr_SIP_Response(template CallidString call_id, template From from_addr, template To 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 From from_addr, template To 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 From from_addr, template To 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 From from_addr, template To 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 } /* 183 Session Progress */ template (present) PDU_SIP_Response tr_SIP_Response_SessionProgress( template (present) CallidString call_id, template (present) From from_addr, template (present) To to_addr, template (present) Via via, template (present) integer seq_nr := ?, template (present) charstring method := "INVITE", template Require require := *, template RSeq rseq := *, template charstring body := *) := { statusLine := tr_SIP_StatusLine(183, "Session Progress"), msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, *, via, method, seq_nr, require := require, rseq := rseq), 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 3310: Same as f_sip_digest_A1(), but using an octet buffer as password (AKAv1-MD5, pwd=RES) */ function f_sip_digest_A1_octpwd(charstring user, charstring realm, octetstring password) return charstring { var charstring A1pre := f_sip_str_unquote(user) & ":" & f_sip_str_unquote(realm) & ":"; var octetstring A1 := char2oct(A1pre) & password; var charstring digestA1 := f_str_tolower(oct2str(f_calculateMD5_oct(A1))); log("A1-octpwd: 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_Response_MD5(charstring user, charstring realm, charstring password, charstring method, charstring uri, charstring qop, charstring nonce, charstring cnonce, charstring nc) return charstring { /* 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 req_digest := f_sip_digest_RequestDigest(digestA1, nonce, nc, cnonce, qop, digestA2); return req_digest; } /* Digest Auth: RFC 2617 */ function f_sip_digest_gen_Authorization_Response_AKAv1MD5(charstring user, charstring realm, octetstring password, charstring method, charstring uri, charstring qop, charstring nonce, charstring cnonce, charstring nc) return charstring { /* RFC 2617 3.2.2.2 A1 */ var charstring digestA1 := f_sip_digest_A1_octpwd(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 req_digest := f_sip_digest_RequestDigest(digestA1, nonce, nc, cnonce, qop, digestA2); return req_digest; } function f_sip_digest_gen_Authorization_MD5(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"; var charstring nc := f_str_tolower(hex2str(int2hex(nc_int, 8))); var charstring req_digest; req_digest := f_sip_digest_gen_Authorization_Response_MD5(user, realm, password, method, uri, selected_qop, nonce, cnonce, nc); cred := ts_Credentials_DigestResponseMD5(user, realm, nonce, uri, req_digest, opaque, algorithm, selected_qop, cnonce, nc); authorization := ts_Authorization(cred); return valueof(authorization); } /* RFC 3310: Same as f_sip_digest_validate_Authorization_MD5(), but using an octet buffer as password (AKAv1-MD5, pwd=RES) */ function f_sip_digest_validate_Authorization_AKAv1MD5(Authorization authorization, charstring method, octetstring password) { var CommaParam_List auth_pars := authorization.body.digestResponse; var template (omit) GenericParam rx_param; rx_param := f_sip_param_find(auth_pars, "algorithm"); if (istemplatekind(rx_param, "omit")) { /* Assume MD5 if not set */ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Algorithm param not present"); } else { var charstring algorithm := valueof(rx_param.paramValue); if (f_strstr(algorithm, "AKAv1-MD5") == -1) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected algorithm: ", algorithm)); } } var charstring user := f_sip_param_get_value_present_or_fail(auth_pars, "username"); var charstring realm := f_sip_param_get_value_present_or_fail(auth_pars, "realm"); var charstring uri := f_sip_param_get_value_present_or_fail(auth_pars, "uri"); var charstring nonce := f_sip_param_get_value_present_or_fail(auth_pars, "nonce"); var charstring qop := f_sip_param_get_value_present_or_fail(auth_pars, "qop"); var charstring response := f_sip_param_get_value_present_or_fail(auth_pars, "response"); var charstring cnonce := f_sip_param_get_value_present_or_fail(auth_pars, "cnonce"); var charstring nc := f_sip_param_get_value_present_or_fail(auth_pars, "nc"); if (f_strstr(qop, "auth") == -1) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Unexpected qop: ", qop)); } var charstring exp_response := f_sip_digest_gen_Authorization_Response_AKAv1MD5(f_sip_str_unquote(user), f_sip_str_unquote(realm), password, method, f_sip_str_unquote(uri), qop, f_sip_str_unquote(nonce), f_sip_str_unquote(cnonce), nc); response := f_sip_str_unquote(response); if (response != exp_response) { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, log2str("Wrong digest response: ", response, " vs exp: ", exp_response, ". Params: ", auth_pars)); } } /* 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_MD5(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 & ":"; if (ispresent(uri.userInfo)) { str := str & uri.userInfo.userOrTelephoneSubscriber & "@"; } str := str & 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_Addr_Union_to_str(Addr_Union addru) return charstring { if (ischosen(addru.nameAddr)) { return f_sip_NameAddr_to_str(addru.nameAddr); } else { return f_sip_SipUrl_to_str(addru.addrSpecUnion); } } function f_sip_SipAddr_to_str(SipAddr sip_addr) return charstring { return f_sip_Addr_Union_to_str(sip_addr.addr); } }