Esempio n. 1
0
/// 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();
  }
}
Esempio n. 2
0
// 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;
}
Esempio n. 3
0
/// 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());
  }
}
Esempio n. 4
0
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;
}
Esempio n. 5
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
// - 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);
}
Esempio n. 7
0
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);
  }
}
Esempio n. 8
0
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)))));
}
Esempio n. 9
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;
}
Esempio n. 10
0
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;
}