/// Returns a canonical IMS public user identity from a URI as per TS 23.003 /// 13.4. std::string PJUtils::public_id_from_uri(const pjsip_uri* uri) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri public_id; memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_sip_uri)); public_id.passwd.slen = 0; public_id.port = 0; public_id.user_param.slen = 0; public_id.method_param.slen = 0; public_id.transport_param.slen = 0; public_id.ttl_param = -1; public_id.lr_param = 0; public_id.maddr_param.slen = 0; public_id.other_param.next = NULL; public_id.header_param.next = NULL; return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id); } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { pjsip_tel_uri public_id; memcpy((char*)&public_id, (char*)uri, sizeof(pjsip_tel_uri)); public_id.context.slen = 0; public_id.ext_param.slen = 0; public_id.isub_param.slen = 0; public_id.other_param.next = NULL; return uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)&public_id); } else { return std::string(); } }
// Determine the default private ID for a public ID contained in a URI. This // is calculated as specified by the 3GPP specs by effectively stripping the // scheme. std::string PJUtils::default_private_id_from_uri(const pjsip_uri* uri) { std::string id; if (PJSIP_URI_SCHEME_IS_SIP(uri) || PJSIP_URI_SCHEME_IS_SIPS(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; if (sip_uri->user.slen > 0) { id = PJUtils::pj_str_to_string(&sip_uri->user) + "@" + PJUtils::pj_str_to_string(&sip_uri->host); } else { id = PJUtils::pj_str_to_string(&sip_uri->host); } } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { id = PJUtils::pj_str_to_string(&((pjsip_tel_uri*)uri)->number) + "@" + PJUtils::pj_str_to_string(&stack_data.default_home_domain); } else { const pj_str_t* scheme = pjsip_uri_get_scheme(uri); LOG_WARNING("Unsupported scheme \"%.*s\" in To header when determining private ID - ignoring", scheme->slen, scheme->ptr); } return id; }
/// Apply the mangalgorithm to the specified URI. The user part of the URI is /// always mangled. The domain is mangled separately, and is only mangled if /// mangelwurzel's change_domain flag is set, or if the function's /// force_mangle_domain flag is set (we use this flag to make sure we always /// mangle the domains for Routes and Record-Routes). We don't mangle /// anything else (e.g. port number, SIP parameters). void MangelwurzelTsx::mangle_uri(pjsip_uri* uri, pj_pool_t* pool, bool force_mangle_domain) { if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; std::string user = PJUtils::pj_str_to_string(&sip_uri->user); mangle_string(user); sip_uri->user = pj_strdup3(pool, user.c_str()); if ((force_mangle_domain) || (_config.change_domain)) { std::string host = PJUtils::pj_str_to_string(&sip_uri->host); mangle_string(host); sip_uri->host = pj_strdup3(pool, host.c_str()); } } else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri; std::string number = PJUtils::pj_str_to_string(&tel_uri->number); mangle_string(number); tel_uri->number = pj_strdup3(pool, number.c_str()); } }
void process_subscription_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); pjsip_msg *msg = rdata->msg_info.msg; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_expires; } if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). LOG_ERROR("Rejecting subscribe request using invalid URI scheme"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } bool emergency_subscription = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { emergency_subscription = PJUtils::is_emergency_registration(contact_hdr); if (!emergency_subscription) { break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_subscription) { // Reject a subscription with a Contact header containing a contact address // that's been registered for emergency service. LOG_ERROR("Rejecting subscribe request from emergency registration"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &STR_ALLOW_EVENTS, &STR_REG); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_EVENT, NULL, (pjsip_hdr*)allow_events_hdr, NULL); return; } ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process SUBSCRIBE for public ID %s", public_id.c_str()); // Get the call identifier from the headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; // Subscriber must have already registered to be making a subscribe std::string state; HTTPCode http_code = hss->get_registration_data(public_id, state, ifc_map, uris, trail); if ((http_code != HTTP_OK) || (state != "REGISTERED")) { // We failed to get the list of associated URIs. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting SUBSCRIBE request"); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("aor = %s", aor.c_str()); LOG_DEBUG("SUBSCRIBE for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); // Write to the local store, checking the remote store if there is no entry locally. If the write to the local store succeeds, then write to the remote store. pjsip_tx_data* tdata_notify = NULL; RegStore::AoR* aor_data = NULL; std::string subscription_id; pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata, now, NULL, remote_store, &tdata_notify, &aor_data, true, subscription_id, trail); if (aor_data != NULL) { // Log the subscriptions. log_subscriptions(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { RegStore::AoR* remote_aor_data = NULL; std::string ignore; write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL, &tdata_notify, &remote_aor_data, false, ignore, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the subscribe with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response LOG_ERROR("Error building SUBSCRIBE %d response %s", st_code, PJUtils::pj_status_to_string(status).c_str()); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Error building SUBSCRIBE (" + std::to_string(st_code) + ") " + PJUtils::pj_status_to_string(status); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(tdata->pool, expiry); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); // Add the to tag to the response pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); pj_strdup2(tdata->pool, &to->tag, subscription_id.c_str()); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response. status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // Send the Notify if (tdata_notify != NULL && notify_status == PJ_SUCCESS) { set_trail(tdata_notify, trail); status = PJUtils::send_request(tdata_notify, 0, NULL, NULL, true); if (status != PJ_SUCCESS) { // LCOV_EXCL_START SAS::Event event(trail, SASEvent::NOTIFICATION_FAILED, 0); std::string error_msg = "Failed to send NOTIFY - error code: " + std::to_string(status); event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
// Determine the type of a URI. // // Parameters: // // - uri - the URI to classify // - prefer_sip - for ambiguous URIs like sip:[email protected] (which could be a global phone // number or just a SIP URI), prefer to interpret it as SIP // - check_np - check for the presence of Number Portability parameters and // classify accordingly // URIClass URIClassifier::classify_uri(const pjsip_uri* uri, bool prefer_sip, bool check_np) { URIClass ret = URIClass::UNKNOWN; // First, check to see if this URI has number portability data - this takes priority bool has_rn = false; bool has_npdi = false; if (check_np) { if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // If the URI is a tel URI, pull out the information from the other_params has_rn = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_NPDI) != NULL); } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { // If the URI is a tel URI, pull out the information from the userinfo_params has_rn = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_NPDI) != NULL); } } if (has_rn) { if (has_npdi) { ret = FINAL_NP_DATA; } else { ret = NP_DATA; } } // No number portability data else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // TEL URIs can only represent phone numbers - decide if it's a global (E.164) number or not pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri; std::string user = PJUtils::pj_str_to_string(&tel_uri->number); boost::match_results<std::string::const_iterator> results; if (boost::regex_match(user, results, CHARS_ALLOWED_IN_GLOBAL_NUM)) { ret = GLOBAL_PHONE_NUMBER; } else { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; } } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; pj_str_t host = sip_uri->host; bool home_domain = is_home_domain(host); bool local_to_node = is_local_name(host); bool is_gruu = (pjsip_param_find(&((pjsip_sip_uri*)uri)->other_param, &STR_GR) != NULL); bool treat_number_as_phone = !enforce_user_phone && !prefer_sip; TRC_DEBUG("home domain: %s, local_to_node: %s, is_gruu: %s, enforce_user_phone: %s, prefer_sip: %s, treat_number_as_phone: %s", home_domain ? "true" : "false", local_to_node ? "true" : "false", is_gruu ? "true" : "false", enforce_user_phone ? "true" : "false", prefer_sip ? "true" : "false", treat_number_as_phone ? "true" : "false"); bool classified = false; // SIP URI that's 'really' a phone number - apply the same logic as for TEL URIs if ((!pj_strcmp(&((pjsip_sip_uri*)uri)->user_param, &STR_USER_PHONE) || (home_domain && treat_number_as_phone && !is_gruu))) { // Get the user part minus any parameters. std::string user = PJUtils::pj_str_to_string(&sip_uri->user); if (!user.empty()) { std::vector<std::string> user_tokens; Utils::split_string(user, ';', user_tokens, 0, true); boost::match_results<std::string::const_iterator> results; if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_GLOBAL_NUM)) { ret = GLOBAL_PHONE_NUMBER; classified = true; } else if (boost::regex_match(user_tokens[0], results, CHARS_ALLOWED_IN_LOCAL_NUM)) { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; classified = true; } } } if (!classified) { // Not a phone number - classify it based on domain ret = (home_domain) ? HOME_DOMAIN_SIP_URI : ((local_to_node) ? NODE_LOCAL_SIP_URI : OFFNET_SIP_URI); } } std::string uri_str = PJUtils::uri_to_string(PJSIP_URI_IN_OTHER, uri); TRC_DEBUG("Classified URI %s as %d", uri_str.c_str(), (int)ret); return ret; }
void SubscriptionSproutletTsx::process_subscription_request(pjsip_msg* req) { SAS::TrailId trail_id = trail(); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(req)->uri); if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). TRC_DEBUG("Rejecting subscribe request using invalid URI scheme"); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0); SAS::report_event(event); pjsip_msg* rsp = create_response(req, PJSIP_SC_NOT_FOUND); send_response(rsp); free_msg(req); return; } // Check if the contact header is present. If it isn't, we want to abort // processing before going any further, to avoid unnecessary work. pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, NULL); if (contact_hdr == NULL) { TRC_DEBUG("Unable to parse contact header from request. " "Aborting processing"); pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_REQUEST); send_response(rsp); free_msg(req); return; } // Check if this is a subscription request from a binding that was emergency // registered. bool emergency_subscription = false; while (contact_hdr != NULL) { emergency_subscription = PJUtils::is_emergency_registration(contact_hdr); if (!emergency_subscription) { break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_subscription) { // Reject a subscription with a Contact header containing a contact address // that's been registered for emergency service. TRC_DEBUG("Rejecting subscribe request from emergency registration"); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0); SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_EVENT); pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(get_pool(rsp), &STR_ALLOW_EVENTS, &STR_REG); pjsip_msg_add_hdr(rsp, (pjsip_hdr*)allow_events_hdr); send_response(rsp); free_msg(req); return; } // At this point we are going to attempt to process the subscribe. // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); TRC_DEBUG("Process SUBSCRIBE for public ID %s", public_id.c_str()); // Get the call identifier from the headers. std::string cid = PJUtils::pj_str_to_string(&PJSIP_MSG_CID_HDR(req)->id); // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail_id, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); // Create an ACR for the request. The node role is always considered // originating for SUBSCRIBE requests. ACR* acr = _subscription->_acr_factory->get_acr(trail_id, ACR::CALLING_PARTY, ACR::NODE_ROLE_ORIGINATING); acr->rx_request(req); // Work out the expiry time of the subscription. pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : SubscriptionSproutlet::DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > _subscription->_max_expires) { // Expiry is too long, set it to the maximum. TRC_DEBUG("Requested expiry (%d) is too long, setting to the maximum (%d)", expiry, _subscription->_max_expires); expiry = _subscription->_max_expires; } // Create a subscription object from the request that we can pass down to // be set into/updated in the different stores Subscription* new_subscription = create_subscription(req, expiry); HSSConnection::irs_info irs_info; HTTPCode rc; // Update or remove the subscription in the subscriber manager depending on // the expiry time. if (expiry != 0) { TRC_DEBUG("Adding/updating the subscription with ID %s", new_subscription->get_id().c_str()); Subscriptions new_subscriptions; new_subscriptions.insert(std::make_pair(new_subscription->get_id(), new_subscription)); rc = _subscription->_sm->update_subscriptions( public_id, new_subscriptions, irs_info, trail_id); } else { TRC_DEBUG("Removing the subscription with ID %s", new_subscription->get_id().c_str()); rc = _subscription->_sm->remove_subscriptions(public_id, {new_subscription->get_id()}, irs_info, trail_id); } pjsip_status_code st_code = determine_sm_sip_response(rc, irs_info._regstate, "SUBSCRIBE"); pjsip_msg* rsp = create_response(req, st_code); if (st_code == PJSIP_SC_OK) { // The subscribe was successful. SAS log, and add headers to the response. TRC_DEBUG("The subscribe has been successful"); SAS::Event sub_accepted(trail_id, SASEvent::SUBSCRIBE_ACCEPTED, 0); SAS::report_event(sub_accepted); // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(get_pool(rsp), expiry); pjsip_msg_add_hdr(rsp, (pjsip_hdr*)expires_hdr); // Add the contact header pjsip_contact_hdr* contact_hdr = pjsip_contact_hdr_create(get_pool(rsp)); pjsip_name_addr* contact_uri = pjsip_name_addr_create(get_pool(rsp)); contact_uri->uri = (pjsip_uri*)stack_data.scscf_uri; contact_hdr->uri = (pjsip_uri*)contact_uri; pjsip_msg_add_hdr(rsp, (pjsip_hdr*)contact_hdr); // Add a P-Charging-Function-Addresses header to the successful SUBSCRIBE // response containing the charging addresses returned by the HSS. PJUtils::add_pcfa_header(rsp, get_pool(rsp), irs_info._ccfs, irs_info._ecfs, false); } else if (st_code == PJSIP_SC_TEMPORARILY_UNAVAILABLE) { // A 480 response means that the subscriber wasn't registered. SAS log // this. TRC_DEBUG("The subscribe has failed as the subscriber (%s) wasn't registered", public_id.c_str()); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_NOT_REG, 0); SAS::report_event(event); } else { // The subscribe was unsuccessful. TRC_DEBUG("The subscribe failed with return code %d", st_code); SAS::Event sub_failed(trail_id, SASEvent::SUBSCRIBE_FAILED, 0); sub_failed.add_var_param(public_id); SAS::report_event(sub_failed); } // Add the to tag to the response (even if the subscribe was rejected). pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(rsp, PJSIP_H_TO, NULL); pj_strdup2(get_pool(rsp), &to->tag, new_subscription->_to_tag.c_str()); // Pass the response to the ACR. acr->tx_response(rsp); // Send the response. send_response(rsp); // Send the ACR and delete it. acr->send(); delete acr; acr = NULL; delete new_subscription; new_subscription = NULL; free_msg(req); }
void ICSCFSproutletTsx::on_rx_initial_request(pjsip_msg* req) { pj_pool_t* pool = get_pool(req); pjsip_route_hdr* hroute = (pjsip_route_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_ROUTE, NULL); // TS 24.229 says I-CSCF processing shouldn't be done if a message has more than one Route header. // We've stripped one off in Sproutlet processing, so check for a second and just forward the // message if it's there. if (hroute != NULL) { send_request(req); return; } pjsip_uri* next_hop = PJUtils::next_hop(req); URIClass next_hop_class = URIClassifier::classify_uri(next_hop); if (req->line.req.method.id == PJSIP_ACK_METHOD && next_hop == req->line.req.uri && ((next_hop_class == NODE_LOCAL_SIP_URI) || (next_hop_class == HOME_DOMAIN_SIP_URI))) { // Ignore ACK messages with no Route headers and a local Request-URI, as: // - the I-CSCF should not be handling these // - we've seen ACKs matching this descrption being generated at overload and looping repeatedly // // This is a fairly targeted fix for https://github.com/Metaswitch/sprout/issues/1091. // TODO: remove this code when #1091 is fixed by other means. free_msg(req); return; } // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for non-REGISTER request"); // Before we clone the request for retries, remove the P-Profile-Key header // if present. PJUtils::remove_hdr(req, &STR_P_PROFILE_KEY); // Determine orig/term and the served user's name. const pjsip_route_hdr* route = route_hdr(); std::string impu; if ((route != NULL) && (pjsip_param_find(&((pjsip_sip_uri*)route->name_addr.uri)->other_param, &STR_ORIG) != NULL)) { // Originating request. TRC_DEBUG("Originating request"); _originating = true; impu = PJUtils::public_id_from_uri(PJUtils::orig_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_ORIG_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } else { // Terminating request. TRC_DEBUG("Terminating request"); _originating = false; pjsip_uri* uri = PJUtils::term_served_user(req); // If the Req URI is a SIP URI with the user=phone parameter set, is not a // GRUU and the user part starts with '+' (i.e. is a global phone number), // we should replace it with a tel URI, as per TS24.229 5.3.2.1. if (PJSIP_URI_SCHEME_IS_SIP(uri)) { URIClass uri_class = URIClassifier::classify_uri(uri); pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; if (uri_class == GLOBAL_PHONE_NUMBER) { TRC_DEBUG("Change request URI from SIP URI to tel URI"); req->line.req.uri = PJUtils::translate_sip_uri_to_tel_uri(sip_uri, pool); } } impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); SAS::Event event(trail(), SASEvent::ICSCF_RCVD_TERM_NON_REG, 0); event.add_var_param(impu); event.add_var_param(req->line.req.method.name.slen, req->line.req.method.name.ptr); SAS::report_event(event); } // Create an LIR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFLIRouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impu, _originating); pjsip_sip_uri* scscf_sip_uri = NULL; // Use the router we just created to query the HSS for an S-CSCF to use. // TS 32.260 Table 5.2.1.1 says an EVENT ACR should be generated on the // completion of a Cx query issued in response to a SIP INVITE bool do_billing = (req->line.req.method.id == PJSIP_INVITE_METHOD); std::string wildcard; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); if ((!_originating) && (scscf_not_found(status_code))) { TRC_DEBUG("Couldn't find an S-CSCF, attempt to translate the URI"); pjsip_uri* uri = PJUtils::term_served_user(req); URIClass uri_class = URIClassifier::classify_uri(uri, false); // For terminating processing, if the HSS indicates that the user does not // exist, and if the request URI is a tel URI, try an ENUM translation. If // this succeeds, go back to the HSS. See TS24.229, 5.3.2.1. // // Before doing that we should check whether the enforce_user_phone flag is // set. If it isn't, and we have a numeric SIP URI, it is possible that // this should have been a tel URI, so translate it and do the HSS lookup // again. Once again, only do this for global numbers. if (PJSIP_URI_SCHEME_IS_SIP(uri) && (uri_class == GLOBAL_PHONE_NUMBER)) { TRC_DEBUG("enforce_user_phone set to false, try using a tel URI"); uri = PJUtils::translate_sip_uri_to_tel_uri((pjsip_sip_uri*)uri, pool); req->line.req.uri = uri; // We need to change the IMPU stored on our LIR router so that when // we do a new LIR we look up the new IMPU. impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(req)); ((ICSCFLIRouter *)_router)->change_impu(impu); status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } if (_icscf->_enum_service) { // If we still haven't found an S-CSCF, we can now try an ENUM lookup. // We put this processing in a loop because in theory we may go round // several times before finding an S-CSCF. In reality this is unlikely // so we set MAX_ENUM_LOOKUPS to 2. for (int ii = 0; (ii < MAX_ENUM_LOOKUPS) && (scscf_not_found(status_code)); ++ii) { if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // Do an ENUM lookup and see if we should translate the TEL URI pjsip_uri* original_req_uri = req->line.req.uri; _icscf->translate_request_uri(req, get_pool(req), trail()); uri = req->line.req.uri; URIClass uri_class = URIClassifier::classify_uri(uri, false, true); std::string rn; if ((uri_class == NP_DATA) || (uri_class == FINAL_NP_DATA)) { // We got number portability information from ENUM - drop out and route to the BGCF. route_to_bgcf(req); return; } else if (pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, original_req_uri, req->line.req.uri) != PJ_SUCCESS) { // The URI has changed, so make sure we do a LIR lookup on it. impu = PJUtils::public_id_from_uri(req->line.req.uri); ((ICSCFLIRouter *)_router)->change_impu(impu); } // If we successfully translate the req URI and end up with either another TEL URI or a // local SIP URI, we should look for an S-CSCF again. if ((uri_class == LOCAL_PHONE_NUMBER) || (uri_class == GLOBAL_PHONE_NUMBER) || (uri_class == HOME_DOMAIN_SIP_URI)) { // TEL or local SIP URI. Look up the S-CSCF again. status_code = (pjsip_status_code)_router->get_scscf(pool, scscf_sip_uri, wildcard, do_billing); } else { // Number translated to off-switch. Drop out of the loop. ii = MAX_ENUM_LOOKUPS; } } else { // Can't translate the number, skip to the end of the loop. ii = MAX_ENUM_LOOKUPS; } } } else { // The user is not in the HSS and ENUM is not configured. TS 24.229 // says that, as an alternative to ENUM, we can "forward the request to // the transit functionality for subsequent routeing". Let's do that // (currently, we assume the BGCF is the transit functionality, but that // may be made configurable in future). TRC_DEBUG("No ENUM service available - outing request directly to transit function (BGCF)"); route_to_bgcf(req); return; } } URIClass uri_class = URIClassifier::classify_uri(req->line.req.uri); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for non-REGISTER"); if (_originating) { // Add the `orig` parameter. pjsip_param* orig_param = PJ_POOL_ALLOC_T(get_pool(req), pjsip_param); pj_strdup(get_pool(req), &orig_param->name, &STR_ORIG); orig_param->value.slen = 0; pj_list_insert_after(&scscf_sip_uri->other_param, orig_param); } // Add the P-Profile-Key header here if we've got a wildcard if (wildcard != "") { add_p_profile_header(wildcard, req); } PJUtils::add_route_header(req, scscf_sip_uri, get_pool(req)); send_request(req); } else if ((uri_class == OFFNET_SIP_URI) || (uri_class == GLOBAL_PHONE_NUMBER)) { // Target is a TEL URI or not in our home domain. Pass to the BGCF. route_to_bgcf(req); } else { // Target is in our home domain, but we failed to find an S-CSCF. This is the final response. pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }
bool PJUtils::is_uri_phone_number(pjsip_uri* uri) { return ((uri != NULL) && ((PJSIP_URI_SCHEME_IS_TEL(uri) || (PJSIP_URI_SCHEME_IS_SIP(uri) && (pj_strcmp2(&((pjsip_sip_uri*)uri)->user_param, "phone") == 0))))); }
// Determine the type of a URI. // // Parameters: // // - uri - the URI to classify // - prefer_sip - for ambiguous URIs like sip:[email protected] (which could be a global phone // number or just a SIP URI), prefer to interpret it as SIP // URIClass URIClassifier::classify_uri(const pjsip_uri* uri, bool prefer_sip) { URIClass ret = URIClass::UNKNOWN; // First, check to see if this URI has number portability data - this takes priority bool has_rn = false; bool has_npdi = false; if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // If the URI is a tel URI, pull out the information from the other_params has_rn = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_tel_uri*)uri)->other_param, &STR_NPDI) != NULL); } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { // If the URI is a tel URI, pull out the information from the userinfo_params has_rn = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_RN) != NULL); has_npdi = (pjsip_param_find(&((pjsip_sip_uri*)uri)->userinfo_param, &STR_NPDI) != NULL); } if (has_rn) { if (has_npdi) { ret = FINAL_NP_DATA; } else { ret = NP_DATA; } } // No number portability data else if (PJSIP_URI_SCHEME_IS_TEL(uri)) { // TEL URIs can only represent phone numbers - decide if it's a global (E.164) number or not pjsip_tel_uri* tel_uri = (pjsip_tel_uri*)uri; if (tel_uri->number.slen > 0 && tel_uri->number.ptr[0] == '+') { ret = GLOBAL_PHONE_NUMBER; } else { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; } } else if (PJSIP_URI_SCHEME_IS_SIP(uri)) { pjsip_sip_uri* sip_uri = (pjsip_sip_uri*)uri; pj_str_t host = sip_uri->host; bool home_domain = is_home_domain(host); bool local_to_node = is_local_name(host); bool is_gruu = (pjsip_param_find(&((pjsip_sip_uri*)uri)->other_param, &STR_GR) != NULL); bool treat_number_as_phone = !enforce_user_phone && !prefer_sip; TRC_DEBUG("home domain: %s, local_to_node: %s, is_gruu: %s, enforce_user_phone: %s, prefer_sip: %s, treat_number_as_phone: %s", home_domain ? "true" : "false", local_to_node ? "true" : "false", is_gruu ? "true" : "false", enforce_user_phone ? "true" : "false", prefer_sip ? "true" : "false", treat_number_as_phone ? "true" : "false"); // SIP URI that's 'really' a phone number - apply the same logic as for TEL URIs if ((!pj_strcmp(&((pjsip_sip_uri*)uri)->user_param, &STR_USER_PHONE) || (home_domain && treat_number_as_phone && !is_gruu))) { if (sip_uri->user.slen > 0 && sip_uri->user.ptr[0] == '+') { ret = GLOBAL_PHONE_NUMBER; } else { ret = enforce_global ? LOCAL_PHONE_NUMBER : GLOBAL_PHONE_NUMBER; } } // Not a phone number - classify it based on domain else if (home_domain) { ret = HOME_DOMAIN_SIP_URI; } else if (local_to_node) { ret = NODE_LOCAL_SIP_URI; } else { ret = OFFNET_SIP_URI; } } TRC_DEBUG("Classified URI as %d", (int)ret); return ret; }
void process_register_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). LOG_ERROR("Rejecting register request using invalid URI scheme"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); // Can't log the public ID as the REGISTER has failed too early std::string public_id = "UNKNOWN"; std::string error_msg = "Rejecting register request using invalid URI scheme"; event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } // Allocate an ACR for this transaction and pass the request to it. ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process REGISTER for public ID %s", public_id.c_str()); // Get the call identifier and the cseq number from the respective headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; pjsip_msg *msg = rdata->msg_info.msg; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; std::string private_id; std::string private_id_for_binding; bool success = get_private_id(rdata, private_id); if (!success) { // There are legitimate cases where we don't have a private ID // here (for example, on a re-registration where Bono has set the // Integrity-Protected header), so this is *not* a failure // condition. // We want the private ID here so that Homestead can use it to // subscribe for updates from the HSS - but on a re-registration, // Homestead should already have subscribed for updates during the // initial registration, so we can just make a request using our // public ID. private_id = ""; // IMS compliant clients will always have the Auth header on all REGISTERs, // including reREGISTERS. Non-IMS clients won't, but their private ID // will always be the public ID with the sip: removed. private_id_for_binding = PJUtils::default_private_id_from_uri(uri); } else { private_id_for_binding = private_id; } SAS::Event event(trail, SASEvent::REGISTER_START, 0); event.add_var_param(public_id); event.add_var_param(private_id); SAS::report_event(event); std::string regstate; std::deque<std::string> ccfs; std::deque<std::string> ecfs; HTTPCode http_code = hss->update_registration_state(public_id, private_id, HSSConnection::REG, regstate, ifc_map, uris, ccfs, ecfs, trail); if ((http_code != HTTP_OK) || (regstate != HSSConnection::STATE_REGISTERED)) { // We failed to register this subscriber at the HSS. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting register request with invalid public/private identity"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid public/private identity"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); int expiry = 0; bool is_initial_registration; // Loop through each contact header. If every registration is an emergency // registration and its expiry is 0 then reject with a 501. // If there are valid registration updates to make then attempt to write to // store, which also stops emergency registrations from being deregistered. bool reject_with_501 = true; bool any_emergency_registrations = false; bool reject_with_400 = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); expiry = (contact_hdr->expires != -1) ? contact_hdr->expires : (expires != NULL) ? expires->ivalue : max_expires; if ((contact_hdr->star) && (expiry != 0)) { // Wildcard contact, which can only be used if the expiry is 0 LOG_ERROR("Attempted to deregister all bindings, but expiry value wasn't 0"); reject_with_400 = true; break; } reject_with_501 = (reject_with_501 && PJUtils::is_emergency_registration(contact_hdr) && (expiry == 0)); any_emergency_registrations = (any_emergency_registrations || PJUtils::is_emergency_registration(contact_hdr)); contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (reject_with_400) { SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid contact header"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_REQUEST, NULL, NULL, NULL); delete acr; return; } if (reject_with_501) { LOG_ERROR("Rejecting register request as attempting to deregister an emergency registration"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting deregister request for emergency registrations"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_IMPLEMENTED, NULL, NULL, NULL); delete acr; return; } // Write to the local store, checking the remote store if there is no entry locally. RegStore::AoR* aor_data = write_to_store(store, aor, rdata, now, expiry, is_initial_registration, NULL, remote_store, true, private_id_for_binding, trail); if (aor_data != NULL) { // Log the bindings. log_bindings(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { int tmp_expiry = 0; bool ignored; RegStore::AoR* remote_aor_data = write_to_store(remote_store, aor, rdata, now, tmp_expiry, ignored, aor_data, NULL, false, private_id_for_binding, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the register with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Unable to access Registration Store"; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response std::string error_msg = "Error building REGISTER " + std::to_string(status) + " response " + PJUtils::pj_status_to_string(status); LOG_ERROR(error_msg.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } if (st_code != PJSIP_SC_OK) { // LCOV_EXCL_START - we only reject REGISTER if something goes wrong, and // we aren't covering any of those paths so we can't hit this either status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "REGISTER failed with status code: " + std::to_string(st_code); event.add_var_param(error_msg); SAS::report_event(event); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add supported and require headers for RFC5626. pjsip_generic_string_hdr* gen_hdr; gen_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_SUPPORTED, &STR_OUTBOUND); if (gen_hdr == NULL) { // LCOV_EXCL_START - can't see how this could ever happen LOG_ERROR("Failed to add RFC 5626 headers"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Failed to add RFC 5636 headers"; event.add_var_param(error_msg); SAS::report_event(event); tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR; pjsip_tx_data_invalidate_msg(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gen_hdr); // Add contact headers for all active bindings. for (RegStore::AoR::Bindings::const_iterator i = aor_data->bindings().begin(); i != aor_data->bindings().end(); ++i) { RegStore::AoR::Binding* binding = i->second; if (binding->_expires > now) { // The binding hasn't expired. Parse the Contact URI from the store, // making sure it is formatted as a name-address. pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool, PJ_TRUE); if (uri != NULL) { // Contact URI is well formed, so include this in the response. pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool); contact->star = 0; contact->uri = uri; contact->q1000 = binding->_priority; contact->expires = binding->_expires - now; pj_list_init(&contact->other_param); for (std::map<std::string, std::string>::iterator j = binding->_params.begin(); j != binding->_params.end(); ++j) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, j->first.c_str()); pj_strdup2(tdata->pool, &new_param->value, j->second.c_str()); pj_list_insert_before(&contact->other_param, new_param); } // The pub-gruu parameter on the Contact header is calculated // from the instance-id, to avoid unnecessary storage in // memcached. std::string gruu = binding->pub_gruu_quoted_string(tdata->pool); if (!gruu.empty()) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, "pub-gruu"); pj_strdup2(tdata->pool, &new_param->value, gruu.c_str()); pj_list_insert_before(&contact->other_param, new_param); } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)contact); } else { // Contact URI is malformed. Log an error, but otherwise don't try and // fix it. // LCOV_EXCL_START hard to hit - needs bad data in the store LOG_WARNING("Badly formed contact URI %s for address of record %s", binding->_uri.c_str(), aor.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Badly formed contact URI - " + binding->_uri; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } } // Deal with path header related fields in the response. pjsip_routing_hdr* path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL); if ((path_hdr != NULL) && (!aor_data->bindings().empty())) { // We have bindings with path headers so we must require outbound. pjsip_require_hdr* require_hdr = pjsip_require_hdr_create(tdata->pool); require_hdr->count = 1; require_hdr->values[0] = STR_OUTBOUND; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)require_hdr); } // Echo back any Path headers as per RFC 3327, section 5.3. We take these // from the request as they may not exist in the bindings any more if the // bindings have expired. while (path_hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, path_hdr)); path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } // Add the Service-Route header. It isn't safe to do this with the // pre-built header from the global pool because the chaining data // structures in the header may get overwritten, but it is safe to do a // shallow clone. pjsip_hdr* clone = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, service_route); pjsip_msg_insert_first_hdr(tdata->msg, clone); // Add P-Associated-URI headers for all of the associated URIs. for (std::vector<std::string>::iterator it = uris.begin(); it != uris.end(); it++) { pjsip_routing_hdr* pau = identity_hdr_create(tdata->pool, STR_P_ASSOCIATED_URI); pau->name_addr.uri = PJUtils::uri_from_string(*it, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pau); } // Add a PCFA header. PJUtils::add_pcfa_header(tdata->msg, tdata->pool, ccfs, ecfs, true); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response, but prevent the transmitted data from being freed, as we may need to inform the // ASes of the 200 OK response we sent. pjsip_tx_data_add_ref(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // TODO in sto397: we should do third-party registration once per // service profile (i.e. once per iFC, using an arbitrary public // ID). hss->get_subscription_data should be enhanced to provide an // appropriate data structure (representing the ServiceProfile // nodes) and we should loop through that. Don't send any register that // contained emergency registrations to the application servers. if (!any_emergency_registrations) { RegistrationUtils::register_with_application_servers(ifc_map[public_id], store, rdata, tdata, expiry, is_initial_registration, public_id, trail); } // Now we can free the tdata. pjsip_tx_data_dec_ref(tdata); LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }