Example #1
0
static int pjsip_name_addr_compare(  pjsip_uri_context_e context,
                                     const pjsip_name_addr *naddr1,
                                     const pjsip_name_addr *naddr2)
{
    int d;

    /* Check that naddr2 is also a name_addr */
    if (naddr1->vptr != naddr2->vptr)
        return -1;

    /* I'm not sure whether display name is included in the comparison. */
    if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
        return -1;
    }

    pj_assert( naddr1->uri != NULL );
    pj_assert( naddr2->uri != NULL );

    /* Compare name-addr as URL */
    d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
    if (d)
        return d;

    return 0;
}
Example #2
0
/*! \brief Callback function for finding a contact */
static int registrar_find_contact(void *obj, void *arg, int flags)
{
	struct ast_sip_contact *contact = obj;
	const struct registrar_contact_details *details = arg;
	pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);

	return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0;
}
static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg)
{
	pjsip_contact_hdr *contact;

	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
	if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

		rewrite_uri(rdata, uri);

		if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact
			|| pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
			dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
			dlg->target = dlg->remote.contact->uri;
		}
		return 0;
	}

	return -1;
}
Example #4
0
static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
{
	pjsip_contact_hdr *contact;

	if (!endpoint) {
		return PJ_FALSE;
	}

	if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) &&
		!contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
		pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
		pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);

		pj_cstr(&uri->host, rdata->pkt_info.src_name);
		if (strcasecmp("udp", rdata->tp_info.transport->type_name)) {
			uri->transport_param = pj_str(rdata->tp_info.transport->type_name);
		} else {
			uri->transport_param.slen = 0;
		}
		uri->port = rdata->pkt_info.src_port;
		ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
			(int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);

		/* rewrite the session target since it may have already been pulled from the contact header */
		if (dlg && (!dlg->remote.contact
			|| pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) {
			dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact);
			dlg->target = dlg->remote.contact->uri;
		}
	}

	if (endpoint->nat.force_rport) {
		rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
	}

	return PJ_FALSE;
}
Example #5
0
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
					        const pjsip_rx_data *rdata,
						unsigned *contact_cnt,
						unsigned max_contact,
						pjsip_contact_hdr *contacts[])
{
    pj_int32_t expiration = NOEXP;
    const pjsip_msg *msg = rdata->msg_info.msg;
    const pjsip_hdr *hdr;

    /* Enumerate all Contact headers in the response */
    *contact_cnt = 0;
    for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
	if (hdr->type == PJSIP_H_CONTACT && 
	    *contact_cnt < max_contact) 
	{
	    contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
	    ++(*contact_cnt);
	}
    }

    if (regc->current_op == REGC_REGISTERING) {
	pj_bool_t has_our_contact = PJ_FALSE;
	const pjsip_expires_hdr *expires;

	/* Get Expires header */
	expires = (const pjsip_expires_hdr*)
		  pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);

	/* Try to find the Contact URIs that we register, in the response
	 * to get the expires value. We'll try both with comparing the URI
	 * and comparing the extension param only.
	 */
	if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
	    unsigned i;
	    for (i=0; i<*contact_cnt; ++i) {
		const pjsip_contact_hdr *our_hdr;

		our_hdr = (const pjsip_contact_hdr*) 
			  regc->contact_hdr_list.next;

		/* Match with our Contact header(s) */
		while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {

		    const pjsip_uri *uri1, *uri2;
		    pj_bool_t matched = PJ_FALSE;

		    /* Exclude the display name when comparing the URI 
		     * since server may not return it.
		     */
		    uri1 = (const pjsip_uri*)
			   pjsip_uri_get_uri(contacts[i]->uri);
		    uri2 = (const pjsip_uri*)
			   pjsip_uri_get_uri(our_hdr->uri);

		    /* First try with exact matching, according to RFC 3261
		     * Section 19.1.4 URI Comparison
		     */
		    if (pjsip_cfg()->regc.check_contact) {
			matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
						uri1, uri2)==0;
		    }

		    /* If no match is found, try with matching the extension
		     * parameter only if extension parameter was added.
		     */
		    if (!matched && regc->add_xuid_param &&
			(PJSIP_URI_SCHEME_IS_SIP(uri1) ||
			 PJSIP_URI_SCHEME_IS_SIPS(uri1)) && 
			(PJSIP_URI_SCHEME_IS_SIP(uri2) ||
			 PJSIP_URI_SCHEME_IS_SIPS(uri2))) 
		    {
			const pjsip_sip_uri *sip_uri1, *sip_uri2;
			const pjsip_param *p1, *p2;
		
			sip_uri1 = (const pjsip_sip_uri*)uri1;
			sip_uri2 = (const pjsip_sip_uri*)uri2;

			p1 = pjsip_param_cfind(&sip_uri1->other_param,
					       &XUID_PARAM_NAME);
			p2 = pjsip_param_cfind(&sip_uri2->other_param,
					       &XUID_PARAM_NAME);
			matched = p1 && p2 &&
				  pj_strcmp(&p1->value, &p2->value)==0;

		    }

		    if (matched) {
			has_our_contact = PJ_TRUE;

			if (contacts[i]->expires >= 0 && 
			    contacts[i]->expires < expiration) 
			{
			    /* Get the lowest expiration time. */
			    expiration = contacts[i]->expires;
			}

			break;
		    }

		    our_hdr = our_hdr->next;

		} /* while ((void.. */

	    }  /* for (i=.. */

	    /* If matching Contact header(s) are found but the
	     * header doesn't contain expires parameter, get the
	     * expiration value from the Expires header. And
	     * if Expires header is not present, get the expiration
	     * value from the request.
	     */
	    if (has_our_contact && expiration == NOEXP) {
		if (expires) {
		    expiration = expires->ivalue;
		} else if (regc->expires_hdr) {
		    expiration = regc->expires_hdr->ivalue;
		} else {
		    /* We didn't request explicit expiration value,
		     * and server doesn't specify it either. This 
		     * shouldn't happen unless we have a broken
		     * registrar.
		     */
		    expiration = 3600;
		}
	    }

	}

	/* If we still couldn't get matching Contact header(s), it means
	 * there must be something wrong with the  registrar (e.g. it may
	 * have modified the URI's in the response, which is prohibited).
	 */
	if (expiration==NOEXP) {
	    /* If the number of Contact headers in the response matches 
	     * ours, they're all probably ours. Get the expiration
	     * from there if this is the case, or from Expires header
	     * if we don't have exact Contact header count, or
	     * from the request as the last resort.
	     */
	    pj_size_t our_contact_cnt;

	    our_contact_cnt = pj_list_size(&regc->contact_hdr_list);

	    if (*contact_cnt == our_contact_cnt && *contact_cnt &&
		contacts[0]->expires >= 0) 
	    {
		expiration = contacts[0]->expires;
	    } else if (expires)
		expiration = expires->ivalue;
	    else if (regc->expires_hdr)
		expiration = regc->expires_hdr->ivalue;
	    else
		expiration = 3600;
	}

    } else {
	/* Just assume that the unregistration has been successful. */
	expiration = 0;
    }

    /* Must have expiration value by now */
    pj_assert(expiration != NOEXP);

    return expiration;
}
Example #6
0
static pj_status_t set_contact( pjsip_regc *regc,
			        int contact_cnt,
				const pj_str_t contact[] )
{
    const pj_str_t CONTACT = { "Contact", 7 };
    pjsip_contact_hdr *h;
    int i;
    
    /* Save existing contact list to removed_contact_hdr_list and
     * clear contact_hdr_list.
     */
    pj_list_merge_last(&regc->removed_contact_hdr_list, 
		       &regc->contact_hdr_list);

    /* Set the expiration of Contacts in to removed_contact_hdr_list 
     * zero.
     */
    h = regc->removed_contact_hdr_list.next;
    while (h != &regc->removed_contact_hdr_list) {
	h->expires = 0;
	h = h->next;
    }

    /* Process new contacts */
    for (i=0; i<contact_cnt; ++i) {
	pjsip_contact_hdr *hdr;
	pj_str_t tmp;

	pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
	hdr = (pjsip_contact_hdr*)
              pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
	if (hdr == NULL) {
	    PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", 
		     (int)tmp.slen, tmp.ptr));
	    return PJSIP_EINVALIDURI;
	}

	/* Find the new contact in old contact list. If found, remove
	 * the old header from the old header list.
	 */
	h = regc->removed_contact_hdr_list.next;
	while (h != &regc->removed_contact_hdr_list) {
	    int rc;

	    rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, 
			       h->uri, hdr->uri);
	    if (rc == 0) {
		/* Match */
		pj_list_erase(h);
		break;
	    }

	    h = h->next;
	}

	/* If add_xuid_param option is enabled and Contact URI is sip/sips,
	 * add xuid parameter to assist matching the Contact URI in the 
	 * REGISTER response later.
	 */
	if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
				     PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) 
	{
	    pjsip_param *xuid_param;
	    pjsip_sip_uri *sip_uri;

	    xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
	    xuid_param->name = XUID_PARAM_NAME;
	    pj_create_unique_string(regc->pool, &xuid_param->value);

	    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
	    pj_list_push_back(&sip_uri->other_param, xuid_param);
	}

	pj_list_push_back(&regc->contact_hdr_list, hdr);
    }

    return PJ_SUCCESS;
}
Example #7
0
/*
 * Test one test entry.
 */
static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry)
{
    pj_status_t status;
    int len;
    char *input;
    pjsip_uri *parsed_uri, *ref_uri;
    pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
    pj_timestamp t1, t2;

    if (entry->len == 0)
	entry->len = pj_ansi_strlen(entry->str);

#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
    input = pj_pool_alloc(pool, entry->len + 1);
    pj_memcpy(input, entry->str, entry->len);
    input[entry->len] = '\0';
#else
    input = entry->str;
#endif

    /* Parse URI text. */
    pj_get_timestamp(&t1);
    var.parse_len = var.parse_len + entry->len;
    parsed_uri = pjsip_parse_uri(pool, input, entry->len, 0);
    if (!parsed_uri) {
	/* Parsing failed. If the entry says that this is expected, then
	 * return OK.
	 */
	status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10;
	if (status != 0) {
	    PJ_LOG(3,(THIS_FILE, "   uri parse error!\n"
				 "   uri='%s'\n",
				 input));
	}
	goto on_return;
    }
    pj_get_timestamp(&t2);
    pj_sub_timestamp(&t2, &t1);
    pj_add_timestamp(&var.parse_time, &t2);

    /* Create the reference URI. */
    ref_uri = entry->creator(pool);

    /* Print both URI. */
    s1.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
    s2.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);

    pj_get_timestamp(&t1);
    len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
    if (len < 1) {
	status = -20;
	goto on_return;
    }
    s1.ptr[len] = '\0';
    s1.slen = len;

    var.print_len = var.print_len + len;
    pj_get_timestamp(&t2);
    pj_sub_timestamp(&t2, &t1);
    pj_add_timestamp(&var.print_time, &t2);

    len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
    if (len < 1) {
	status = -30;
	goto on_return;
    }
    s2.ptr[len] = '\0';
    s2.slen = len;

    /* Full comparison of parsed URI with reference URI. */
    pj_get_timestamp(&t1);
    status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri);
    if (status != 0) {
	/* Not equal. See if this is the expected status. */
	status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40;
	if (status != 0) {
	    PJ_LOG(3,(THIS_FILE, "   uri comparison mismatch, status=%d:\n"
				 "    uri1='%s'\n"
				 "    uri2='%s'",
				 status, s1.ptr, s2.ptr));
	}
	goto on_return;

    } else {
	/* Equal. See if this is the expected status. */
	status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50;
	if (status != PJ_SUCCESS) {
	    goto on_return;
	}
    }

    var.cmp_len = var.cmp_len + len;
    pj_get_timestamp(&t2);
    pj_sub_timestamp(&t2, &t1);
    pj_add_timestamp(&var.cmp_time, &t2);

    /* Compare text. */
    if (entry->printed) {
	if (pj_strcmp2(&s1, entry->printed) != 0) {
	    /* Not equal. */
	    PJ_LOG(3,(THIS_FILE, "   uri print mismatch:\n"
				 "    printed='%s'\n"
				 "    expectd='%s'",
				 s1.ptr, entry->printed));
	    status = -60;
	}
    } else {
	if (pj_strcmp(&s1, &s2) != 0) {
	    /* Not equal. */
	    PJ_LOG(3,(THIS_FILE, "   uri print mismatch:\n"
				 "    uri1='%s'\n"
				 "    uri2='%s'",
				 s1.ptr, s2.ptr));
	    status = -70;
	}
    }

on_return:
    return status;
}
Example #8
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);
  }
}