예제 #1
0
파일: publishc.c 프로젝트: CryptoCall/pjsip
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, 
					pjsip_tx_data *tdata)
{
    pj_status_t status;
    pjsip_cseq_hdr *cseq_hdr;
    pj_uint32_t cseq;

    PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL);

    /* Make sure we don't have pending transaction. */
    pj_mutex_lock(pubc->mutex);
    if (pubc->pending_tsx) {
	if (pubc->opt.queue_request) {
	    pj_list_push_back(&pubc->pending_reqs, tdata);
	    pj_mutex_unlock(pubc->mutex);
	    PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another "
				 "transaction pending"));
	    return PJ_EPENDING;
	} else {
	    pjsip_tx_data_dec_ref(tdata);
	    pj_mutex_unlock(pubc->mutex);
	    PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
				 "transaction pending"));
	    return PJ_EBUSY;
	}
    }
    pj_mutex_unlock(pubc->mutex);

    /* If via_addr is set, use this address for the Via header. */
    if (pubc->via_addr.host.slen > 0) {
        tdata->via_addr = pubc->via_addr;
        tdata->via_tp = pubc->via_tp;
    }

    /* Invalidate message buffer. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Increment CSeq */
    cseq = ++pubc->cseq_hdr->cseq;
    cseq_hdr = (pjsip_cseq_hdr*)
    	       pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
    cseq_hdr->cseq = cseq;

    /* Increment pending transaction first, since transaction callback
     * may be called even before send_request() returns!
     */
    ++pubc->pending_tsx;
    status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, 
				      &tsx_callback);
    if (status!=PJ_SUCCESS) {
	// no need to decrement, callback has been called and it should
	// already decremented pending_tsx. Decrementing this here may 
	// cause accessing freed memory location.
	//--pubc->pending_tsx;
	PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
    }

    return status;
}
예제 #2
0
PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, 
					pjsip_tx_data *tdata)
{
    pj_status_t status;
    pjsip_cseq_hdr *cseq_hdr;
    pj_uint32_t cseq;

    /* Make sure we don't have pending transaction. */
    if (pubc->pending_tsx) {
	PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another "
			     "transaction pending"));
	pjsip_tx_data_dec_ref( tdata );
	return PJSIP_EBUSY;
    }

    /* Invalidate message buffer. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Increment CSeq */
    cseq = ++pubc->cseq_hdr->cseq;
    cseq_hdr = (pjsip_cseq_hdr*)
    	       pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
    cseq_hdr->cseq = cseq;

    /* Increment pending transaction first, since transaction callback
     * may be called even before send_request() returns!
     */
    ++pubc->pending_tsx;
    status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, 
				      &tsx_callback);
    if (status!=PJ_SUCCESS) {
	// no need to decrement, callback has been called and it should
	// already decremented pending_tsx. Decrementing this here may 
	// cause accessing freed memory location.
	//--pubc->pending_tsx;
	PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
    }

    return status;
}
예제 #3
0
파일: sip_reg.c 프로젝트: iamroger/voip
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
{
    pj_status_t status;
    pjsip_cseq_hdr *cseq_hdr;
    pjsip_expires_hdr *expires_hdr;
    pj_uint32_t cseq;

    pj_atomic_inc(regc->busy_ctr);
    pj_lock_acquire(regc->lock);

    /* Make sure we don't have pending transaction. */
    if (regc->has_tsx) {
	PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
			     "transaction pending"));
	pjsip_tx_data_dec_ref( tdata );
	pj_lock_release(regc->lock);
	pj_atomic_dec(regc->busy_ctr);
	return PJSIP_EBUSY;
    }

    pj_assert(regc->current_op == REGC_IDLE);

    /* Invalidate message buffer. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Increment CSeq */
    cseq = ++regc->cseq_hdr->cseq;
    cseq_hdr = (pjsip_cseq_hdr*)
	       pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
    cseq_hdr->cseq = cseq;

    /* Find Expires header */
    expires_hdr = (pjsip_expires_hdr*)
		  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);

    /* Bind to transport selector */
    pjsip_tx_data_set_transport(tdata, &regc->tp_sel);

    regc->has_tsx = PJ_TRUE;

    /* Set current operation based on the value of Expires header */
    if (expires_hdr && expires_hdr->ivalue==0)
	regc->current_op = REGC_UNREGISTERING;
    else
	regc->current_op = REGC_REGISTERING;

    /* Prevent deletion of tdata, e.g: when something wrong in sending,
     * we need tdata to retrieve the transport.
     */
    pjsip_tx_data_add_ref(tdata);

    /* If via_addr is set, use this address for the Via header. */
    if (regc->via_addr.host.slen > 0) {
        tdata->via_addr = regc->via_addr;
        tdata->via_tp = regc->via_tp;
    }

    /* Need to unlock the regc temporarily while sending the message to
     * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
     * It should be safe to do this since the regc's refcount has been
     * incremented.
     */
    pj_lock_release(regc->lock);

    /* Now send the message */
    status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
				      regc, &regc_tsx_callback);
    if (status!=PJ_SUCCESS) {
	PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
    }

    /* Reacquire the lock */
    pj_lock_acquire(regc->lock);

    /* Get last transport used and add reference to it */
    if (tdata->tp_info.transport != regc->last_transport &&
	status==PJ_SUCCESS)
    {
	if (regc->last_transport) {
	    pjsip_transport_dec_ref(regc->last_transport);
	    regc->last_transport = NULL;
	}

	if (tdata->tp_info.transport) {
	    regc->last_transport = tdata->tp_info.transport;
	    pjsip_transport_add_ref(regc->last_transport);
	}
    }

    /* Release tdata */
    pjsip_tx_data_dec_ref(tdata);

    pj_lock_release(regc->lock);

    /* Delete the record if user destroy regc during the callback. */
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
	pjsip_regc_destroy(regc);
    }

    return status;
}
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
{
	pjsip_tpmgr_fla2_param prm;
	pjsip_transport *transport = NULL;
	pjsip_cseq_hdr *cseq;
	pjsip_via_hdr *via;

	/* Use the destination information to determine what local interface this message will go out on */
	pjsip_tpmgr_fla2_param_default(&prm);
	prm.tp_type = tdata->tp_info.transport->key.type;
	pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
	prm.local_if = PJ_TRUE;

	/* If we can't get the local address use best effort and let it pass */
	if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
		return PJ_SUCCESS;
	}

	/* If the transport it is going out on is different reflect it in the message */
	if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
		tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
		transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);
	}

	/* If no new transport use the one provided by the message */
	if (!transport) {
		transport = tdata->tp_info.transport;
	}

	/* If the message should not be rewritten then abort early */
	if (!multihomed_rewrite_header(&prm.ret_addr, transport)) {
		return PJ_SUCCESS;
	}

	/* Update the transport in case it has changed - we do this now in case we don't want to touch the message above */
	tdata->tp_info.transport = transport;

	/* If the message needs to be updated with new address do so */
	if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
		pj_strcmp2(&cseq->method.name, "REGISTER")) {
		pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
			pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

			/* prm.ret_addr is allocated from the tdata pool so it is perfectly fine to just do an assignment like this */
			pj_strassign(&uri->host, &prm.ret_addr);
			uri->port = prm.ret_port;

			pjsip_tx_data_invalidate_msg(tdata);
		}
	}

	if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
		pj_strassign(&via->sent_by.host, &prm.ret_addr);
		via->sent_by.port = prm.ret_port;

		pjsip_tx_data_invalidate_msg(tdata);
	}

	/* Update the SDP if it is present */
	if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
		multihomed_rewrite_sdp(tdata->msg->body->data)) {
		struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
		int stream;

		pj_strassign(&sdp->conn->addr, &prm.ret_addr);

		for (stream = 0; stream < sdp->media_count; ++stream) {
			if (sdp->media[stream]->conn) {
				pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
			}
		}

		pjsip_tx_data_invalidate_msg(tdata);
	}

	return PJ_SUCCESS;
}
/* Callback to be called to handle transaction state changed. */
static void tu_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
{
    struct uac_data *uac_data;
    pj_status_t status;

    if (tsx->role == PJSIP_ROLE_UAS) {
	if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
	    struct uas_data *uas_data;

	    uas_data = (struct uas_data*) tsx->mod_data[mod_tu.id];
	    if (uas_data->uac_tsx) {
		uac_data = (struct uac_data*)
			   uas_data->uac_tsx->mod_data[mod_tu.id];
		uac_data->uas_tsx = NULL;
	    }
		       
	}
	return;
    }

    /* Get the data that we attached to the UAC transaction previously */
    uac_data = (struct uac_data*) tsx->mod_data[mod_tu.id];


    /* Handle incoming response */
    if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {

	pjsip_rx_data *rdata;
	pjsip_response_addr res_addr;
        pjsip_via_hdr *hvia;
	pjsip_tx_data *tdata;

	rdata = event->body.tsx_state.src.rdata;

	/* Do not forward 100 response for INVITE (we already responded
	 * INVITE with 100)
	 */
	if (tsx->method.id == PJSIP_INVITE_METHOD && 
	    rdata->msg_info.msg->line.status.code == 100)
	{
	    return;
	}

	/* Create response to be forwarded upstream 
	 * (Via will be stripped here) 
	 */
	status = pjsip_endpt_create_response_fwd(global.endpt, rdata, 0, 
						 &tdata);
	if (status != PJ_SUCCESS) {
	    app_perror("Error creating response", status);
	    return;
	}

	/* Get topmost Via header of the new response */
	hvia = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, 
						   NULL);
	if (hvia == NULL) {
	    /* Invalid response! Just drop it */
	    pjsip_tx_data_dec_ref(tdata);
	    return;
	}

	/* Calculate the address to forward the response */
	pj_bzero(&res_addr, sizeof(res_addr));
	res_addr.dst_host.type = PJSIP_TRANSPORT_UDP;
	res_addr.dst_host.flag = 
	    pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);

	/* Destination address is Via's received param */
	res_addr.dst_host.addr.host = hvia->recvd_param;
	if (res_addr.dst_host.addr.host.slen == 0) {
	    /* Someone has messed up our Via header! */
	    res_addr.dst_host.addr.host = hvia->sent_by.host;
	}

	/* Destination port is the rport */
	if (hvia->rport_param != 0 && hvia->rport_param != -1)
	    res_addr.dst_host.addr.port = hvia->rport_param;

	if (res_addr.dst_host.addr.port == 0) {
	    /* Ugh, original sender didn't put rport!
	     * At best, can only send the response to the port in Via.
	     */
	    res_addr.dst_host.addr.port = hvia->sent_by.port;
	}

	/* Forward response with the UAS transaction */
	pjsip_tsx_send_msg(uac_data->uas_tsx, tdata);

    }

    /* If UAC transaction is terminated, terminate the UAS as well.
     * This could happen because of:
     *	- timeout on the UAC side
     *  - receipt of 2xx response to INVITE
     */
    if (tsx->state == PJSIP_TSX_STATE_TERMINATED && uac_data &&
	uac_data->uas_tsx) 
    {

	pjsip_transaction *uas_tsx;
	struct uas_data *uas_data;

	uas_tsx = uac_data->uas_tsx;
	uas_data = (struct uas_data*) uas_tsx->mod_data[mod_tu.id];
	uas_data->uac_tsx = NULL;

	if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) {

	    /* Send 408/Timeout if this is an INVITE transaction, since
	     * we must have sent provisional response before. For non
	     * INVITE transaction, just destroy it.
	     */
	    if (tsx->method.id == PJSIP_INVITE_METHOD) {

		pjsip_tx_data *tdata = uas_tsx->last_tx;

		tdata->msg->line.status.code = PJSIP_SC_REQUEST_TIMEOUT;
		tdata->msg->line.status.reason = pj_str("Request timed out");
		tdata->msg->body = NULL;

		pjsip_tx_data_add_ref(tdata);
		pjsip_tx_data_invalidate_msg(tdata);

		pjsip_tsx_send_msg(uas_tsx, tdata);

	    } else {
		/* For non-INVITE, just destroy the UAS transaction */
		pjsip_tsx_terminate(uas_tsx, PJSIP_SC_REQUEST_TIMEOUT);
	    }

	} else if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {

	    if (uas_tsx->state < PJSIP_TSX_STATE_TERMINATED) {
		pjsip_msg *msg;
		int code;

		msg = event->body.tsx_state.src.rdata->msg_info.msg;
		code = msg->line.status.code;

		uac_data->uas_tsx = NULL;
		pjsip_tsx_terminate(uas_tsx, code);
	    }
	}
    }
}
예제 #6
0
/* Reinitialize outgoing request after 401/407 response is received.
 * The purpose of this function is:
 *  - to add a Authorization/Proxy-Authorization header.
 *  - to put the newly created Authorization/Proxy-Authorization header
 *    in cached_list.
 */
PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req(	pjsip_auth_clt_sess *sess,
						const pjsip_rx_data *rdata,
						pjsip_tx_data *old_request,
						pjsip_tx_data **new_request )
{
    pjsip_tx_data *tdata;
    const pjsip_hdr *hdr;
    unsigned chal_cnt;
    pjsip_via_hdr *via;
    pj_status_t status;

    PJ_ASSERT_RETURN(sess && rdata && old_request && new_request,
		     PJ_EINVAL);
    PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED);
    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG,
		     PJSIP_ENOTRESPONSEMSG);
    PJ_ASSERT_RETURN(old_request->msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);
    PJ_ASSERT_RETURN(rdata->msg_info.msg->line.status.code == 401 ||
		     rdata->msg_info.msg->line.status.code == 407,
		     PJSIP_EINVALIDSTATUS);

    tdata = old_request;
    tdata->auth_retry = PJ_FALSE;

    /*
     * Respond to each authentication challenge.
     */
    hdr = rdata->msg_info.msg->hdr.next;
    chal_cnt = 0;
    while (hdr != &rdata->msg_info.msg->hdr) {
	pjsip_cached_auth *cached_auth;
	const pjsip_www_authenticate_hdr *hchal;
	pjsip_authorization_hdr *hauth;

	/* Find WWW-Authenticate or Proxy-Authenticate header. */
	while (hdr != &rdata->msg_info.msg->hdr &&
	       hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
	       hdr->type != PJSIP_H_PROXY_AUTHENTICATE)
	{
	    hdr = hdr->next;
	}
	if (hdr == &rdata->msg_info.msg->hdr)
	    break;

	hchal = (const pjsip_www_authenticate_hdr*) hdr;
	++chal_cnt;

	/* Find authentication session for this realm, create a new one
	 * if not present.
	 */
	cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm );
	if (!cached_auth) {
	    cached_auth = PJ_POOL_ZALLOC_T( sess->pool, pjsip_cached_auth);
	    pj_strdup( sess->pool, &cached_auth->realm, &hchal->challenge.common.realm);
	    cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
#	    if (PJSIP_AUTH_HEADER_CACHING)
	    {
		pj_list_init(&cached_auth->cached_hdr);
	    }
#	    endif
	    pj_list_insert_before( &sess->cached_auth, cached_auth );
	}

	/* Create authorization header for this challenge, and update
	 * authorization session.
	 */
	status = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, 
			       tdata, sess, cached_auth, &hauth);
	if (status != PJ_SUCCESS)
	    return status;

	/* Add to the message. */
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);

	/* Process next header. */
	hdr = hdr->next;
    }

    /* Check if challenge is present */
    if (chal_cnt == 0)
	return PJSIP_EAUTHNOCHAL;

    /* Remove branch param in Via header. */
    via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
    via->branch_param.slen = 0;

    /* Restore strict route set.
     * See http://trac.pjsip.org/repos/ticket/492
     */
    pjsip_restore_strict_route_set(tdata);

    /* Must invalidate the message! */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Retrying.. */
    tdata->auth_retry = PJ_TRUE;

    /* Increment reference counter. */
    pjsip_tx_data_add_ref(tdata);

    /* Done. */
    *new_request = tdata;
    return PJ_SUCCESS;

}
예제 #7
0
/// Retry the request to an alternate S-CSCF if possible.
bool ICSCFProxy::UASTsx::retry_to_alternate_scscf(int rsp_status)
{
  bool retry = false;

  if (_case == SessionCase::REGISTER)
  {
    // Check whether conditions are satisfied for retrying a REGISTER (see
    // 5.3.1.3/TS24.229).
    LOG_DEBUG("Check retry conditions for REGISTER request, status code = %d",
              rsp_status);
    if (((rsp_status >= 300) && (rsp_status <= 399)) ||
        (rsp_status == PJSIP_SC_REQUEST_TIMEOUT) ||
        (rsp_status == PJSIP_SC_TEMPORARILY_UNAVAILABLE))
    {
      // Can do a retry (we support service restoration, so integrity-protected
      // settings in Authorization header are immaterial).
      LOG_DEBUG("Attempt retry to alternate S-CSCF for REGISTER request");
      retry = true;

      std::string st_code = std::to_string(rsp_status);
      SAS::Event event(trail(), SASEvent::SCSCF_RETRY, 0);
      std::string method = "REGISTER";
      event.add_var_param(method);
      event.add_var_param(st_code);
      SAS::report_event(event);
    }
  }
  else
  {
    // Check whether conditions are satisfied for retrying a Non-REGISTER.
    LOG_DEBUG("Check retry conditions for Non-REGISTER request, status code = %d",
              rsp_status);

    if (rsp_status == PJSIP_SC_REQUEST_TIMEOUT)
    {
      LOG_DEBUG("Attempt retry to alternate S-CSCF for non-REGISTER request");
      retry = true;

      std::string st_code = std::to_string(rsp_status);
      SAS::Event event(trail(), SASEvent::SCSCF_RETRY, 0);
      std::string method = "NON-REGISTER";
      event.add_var_param(method);
      event.add_var_param(st_code);
      SAS::report_event(event);
    }
  }

  if (retry)
  {
    // Retry conditions are satisfied, so try to calculate a new target.
    int status_code = calculate_targets();

    if (status_code == PJSIP_SC_OK)
    {
      // We found a suitable alternate S-CSCF and have programmed it as a
      // target, so action the retry.
      forward_request();
    }
    else
    {
      // Failed to find another S-CSCF for the request.
      LOG_DEBUG("Failed to find alternate S-CSCF for retry");
      retry = false;

      if (_case == SessionCase::REGISTER)
      {
        // In the register case the spec's are quite particular about how
        // failures are reported.
        if (status_code == PJSIP_SC_FORBIDDEN)
        {
          // The HSS has returned a negative response to the user registration
          // request - I-CSCF should respond with 403.
          _best_rsp->msg->line.status.code = PJSIP_SC_FORBIDDEN;
          _best_rsp->msg->line.status.reason =
                       *pjsip_get_status_text(_best_rsp->msg->line.status.code);
        }
        else
        {
          // The I-CSCF can't select an S-CSCF for the REGISTER request (either
          // because there are no more S-CSCFs that meet the mandatory
          // capabilitires, or the HSS is temporarily unavailable). There was at
          // least one valid S-CSCF (as this is retry processing). The I-CSCF
          //  must return 504 (TS 24.229, 5.3.1.3) in this case.
          _best_rsp->msg->line.status.code = PJSIP_SC_SERVER_TIMEOUT;
          _best_rsp->msg->line.status.reason =
                       *pjsip_get_status_text(_best_rsp->msg->line.status.code);
        }
        pjsip_tx_data_invalidate_msg(_best_rsp);
      }
    }
  }

  return retry;
}
예제 #8
0
static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
{
	pjsip_tpmgr_fla2_param prm;
	pjsip_cseq_hdr *cseq;
	pjsip_via_hdr *via;

	/* Use the destination information to determine what local interface this message will go out on */
	pjsip_tpmgr_fla2_param_default(&prm);
	prm.tp_type = tdata->tp_info.transport->key.type;
	pj_strset2(&prm.dst_host, tdata->tp_info.dst_name);
	prm.local_if = PJ_TRUE;

	/* If we can't get the local address use best effort and let it pass */
	if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) {
		return PJ_SUCCESS;
	}

	/* The port in the message should always be that of the original transport */
	prm.ret_port = tdata->tp_info.transport->local_name.port;

	/* If the IP source differs from the existing transport see if we need to update it */
	if (pj_strcmp(&prm.ret_addr, &tdata->tp_info.transport->local_name.host)) {

		/* If the transport it is going out on is different reflect it in the message */
		if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP ||
			tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) {
			pjsip_transport *transport;

			transport = multihomed_get_udp_transport(&prm.ret_addr, prm.ret_port);

			if (transport) {
				tdata->tp_info.transport = transport;
			}
		}

		/* If the chosen transport is not bound to any we can't use the source address as it won't get back to us */
		if (!multihomed_bound_any(tdata->tp_info.transport)) {
			pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
		}
	} else {
		/* The transport chosen will deliver this but ensure it is updated with the right information */
		pj_strassign(&prm.ret_addr, &tdata->tp_info.transport->local_name.host);
	}

	/* If the message needs to be updated with new address do so */
	if (tdata->msg->type == PJSIP_REQUEST_MSG || !(cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) ||
		pj_strcmp2(&cseq->method.name, "REGISTER")) {
		pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
		if (contact && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))
			&& !(tdata->msg->type == PJSIP_RESPONSE_MSG && tdata->msg->line.status.code / 100 == 3)) {
			pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);

			/* prm.ret_addr is allocated from the tdata pool OR the transport so it is perfectly fine to just do an assignment like this */
			pj_strassign(&uri->host, &prm.ret_addr);
			uri->port = prm.ret_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);

			pjsip_tx_data_invalidate_msg(tdata);
		}
	}

	if (tdata->msg->type == PJSIP_REQUEST_MSG && (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) {
		pj_strassign(&via->sent_by.host, &prm.ret_addr);
		via->sent_by.port = prm.ret_port;

		pjsip_tx_data_invalidate_msg(tdata);
	}

	/* Update the SDP if it is present */
	if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") &&
		multihomed_rewrite_sdp(tdata->msg->body->data)) {
		struct pjmedia_sdp_session *sdp = tdata->msg->body->data;
		int stream;

		pj_strassign(&sdp->conn->addr, &prm.ret_addr);

		for (stream = 0; stream < sdp->media_count; ++stream) {
			if (sdp->media[stream]->conn) {
				pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr);
			}
		}

		pjsip_tx_data_invalidate_msg(tdata);
	}

	return PJ_SUCCESS;
}
예제 #9
0
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
{
    pj_status_t status;
    pjsip_cseq_hdr *cseq_hdr;
    pjsip_expires_hdr *expires_hdr;
    pj_uint32_t cseq;

    pj_atomic_inc(regc->busy_ctr);
    pj_lock_acquire(regc->lock);

    /* Make sure we don't have pending transaction. */
    if (regc->has_tsx) {
	PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
			     "transaction pending"));
	pjsip_tx_data_dec_ref( tdata );
	pj_lock_release(regc->lock);
	pj_atomic_dec(regc->busy_ctr);
	return PJSIP_EBUSY;
    }

    pj_assert(regc->current_op == REGC_IDLE);

    /* Invalidate message buffer. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Increment CSeq */
    cseq = ++regc->cseq_hdr->cseq;
    cseq_hdr = (pjsip_cseq_hdr*)
	       pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
    cseq_hdr->cseq = cseq;

    /* Find Expires header */
    expires_hdr = (pjsip_expires_hdr*)
		  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);

    /* Bind to transport selector */
    pjsip_tx_data_set_transport(tdata, &regc->tp_sel);

    regc->has_tsx = PJ_TRUE;

    /* Set current operation based on the value of Expires header */
    if (expires_hdr && expires_hdr->ivalue==0)
	regc->current_op = REGC_UNREGISTERING;
    else
	regc->current_op = REGC_REGISTERING;

    status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
				      regc, &tsx_callback);
    if (status!=PJ_SUCCESS) {
	PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
    }

    pj_lock_release(regc->lock);

    /* Delete the record if user destroy regc during the callback. */
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
	pjsip_regc_destroy(regc);
    }

    return status;
}
예제 #10
0
파일: registrar.cpp 프로젝트: jmmL/sprout
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;
}
예제 #11
0
void create_challenge(pjsip_authorization_hdr* auth_hdr,
                      std::string resync,
                      pjsip_rx_data* rdata,
                      pjsip_tx_data* tdata)
{
  // Get the public and private identities from the request.
  std::string impi;
  std::string impu;
  std::string nonce;

  PJUtils::get_impi_and_impu(rdata, impi, impu);
  // Set up the authorization type, following Annex P.4 of TS 33.203.  Currently
  // only support AKA and SIP Digest, so only implement the subset of steps
  // required to distinguish between the two.
  std::string auth_type;
  if (auth_hdr != NULL)
  {
    pjsip_param* integrity =
           pjsip_param_find(&auth_hdr->credential.digest.other_param,
                            &STR_INTEGRITY_PROTECTED);

    if ((integrity != NULL) &&
        ((pj_stricmp(&integrity->value, &STR_YES) == 0) ||
         (pj_stricmp(&integrity->value, &STR_NO) == 0)))
    {
      // Authentication scheme is AKA.
      auth_type = "aka";
    }
  }

  // Get the Authentication Vector from the HSS.
  Json::Value* av = NULL;
  HTTPCode http_code = hss->get_auth_vector(impi, impu, auth_type, resync, av, get_trail(rdata));

  if ((av != NULL) &&
      (!verify_auth_vector(av, impi, get_trail(rdata))))
  {
    // Authentication Vector is badly formed.
    delete av;
    av = NULL;
  }

  if (av != NULL)
  {
    // Retrieved a valid authentication vector, so generate the challenge.
    LOG_DEBUG("Valid AV - generate challenge");
    char buf[16];
    pj_str_t random;
    random.ptr = buf;
    random.slen = sizeof(buf);

    LOG_DEBUG("Create WWW-Authenticate header");
    pjsip_www_authenticate_hdr* hdr = pjsip_www_authenticate_hdr_create(tdata->pool);

    // Set up common fields for Digest and AKA cases (both are considered
    // Digest authentication).
    hdr->scheme = STR_DIGEST;

    if (av->isMember("aka"))
    {
      // AKA authentication.
      LOG_DEBUG("Add AKA information");

      SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE, 0);
      std::string AKA = "AKA";
      event.add_var_param(AKA);
      SAS::report_event(event);

      Json::Value& aka = (*av)["aka"];

      // Use default realm for AKA as not specified in the AV.
      pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &aka_realm);
      hdr->challenge.digest.algorithm = STR_AKAV1_MD5;
      nonce = aka["challenge"].asString();
      pj_strdup2(tdata->pool, &hdr->challenge.digest.nonce, nonce.c_str());
      pj_create_random_string(buf, sizeof(buf));
      pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
      hdr->challenge.digest.qop = STR_AUTH;
      hdr->challenge.digest.stale = PJ_FALSE;

      // Add the cryptography key parameter.
      pjsip_param* ck_param = (pjsip_param*)pj_pool_alloc(tdata->pool, sizeof(pjsip_param));
      ck_param->name = STR_CK;
      std::string ck = "\"" + aka["cryptkey"].asString() + "\"";
      pj_strdup2(tdata->pool, &ck_param->value, ck.c_str());
      pj_list_insert_before(&hdr->challenge.digest.other_param, ck_param);

      // Add the integrity key parameter.
      pjsip_param* ik_param = (pjsip_param*)pj_pool_alloc(tdata->pool, sizeof(pjsip_param));
      ik_param->name = STR_IK;
      std::string ik = "\"" + aka["integritykey"].asString() + "\"";
      pj_strdup2(tdata->pool, &ik_param->value, ik.c_str());
      pj_list_insert_before(&hdr->challenge.digest.other_param, ik_param);
    }
    else
    {
      // Digest authentication.
      LOG_DEBUG("Add Digest information");

      SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE, 0);
      std::string DIGEST = "DIGEST";
      event.add_var_param(DIGEST);
      SAS::report_event(event);

      Json::Value& digest = (*av)["digest"];
      pj_strdup2(tdata->pool, &hdr->challenge.digest.realm, digest["realm"].asCString());
      hdr->challenge.digest.algorithm = STR_MD5;
      pj_create_random_string(buf, sizeof(buf));
      nonce.assign(buf, sizeof(buf));
      pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
      pj_create_random_string(buf, sizeof(buf));
      pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
      pj_strdup2(tdata->pool, &hdr->challenge.digest.qop, digest["qop"].asCString());
      hdr->challenge.digest.stale = PJ_FALSE;
    }

    // Add the header to the message.
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);

    // Store the branch parameter in memcached for correlation purposes
    pjsip_via_hdr* via_hdr = (pjsip_via_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
    std::string branch = (via_hdr != NULL) ? PJUtils::pj_str_to_string(&via_hdr->branch_param) : "";

    (*av)["branch"] = branch;

    // Write the authentication vector (as a JSON string) into the AV store.
    LOG_DEBUG("Write AV to store");
    uint64_t cas = 0;
    bool success = av_store->set_av(impi, nonce, av, cas, get_trail(rdata));
    if (success)
    {
      // We've written the AV into the store, so need to set a Chronos
      // timer so that an AUTHENTICATION_TIMEOUT SAR is sent to the
      // HSS when it expires.
      std::string timer_id;
      std::string chronos_body = "{\"impi\": \"" + impi + "\", \"impu\": \"" + impu +"\", \"nonce\": \"" + nonce +"\"}";
      LOG_DEBUG("Sending %s to Chronos to set AV timer", chronos_body.c_str());
      chronos->send_post(timer_id, 30, "/authentication-timeout", chronos_body, 0);
    }

    delete av;
  }
  else
  {
    std::string error_msg;

    // If we couldn't get the AV because a downstream node is overloaded then don't return
    // a 4xx error to the client.
    if ((http_code == HTTP_SERVER_UNAVAILABLE) || (http_code == HTTP_GATEWAY_TIMEOUT))
    {
      error_msg = "Downstream node is overloaded or unresponsive, unable to get Authentication vector";
      LOG_DEBUG(error_msg.c_str());
      tdata->msg->line.status.code = PJSIP_SC_SERVER_TIMEOUT;
      tdata->msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_SERVER_TIMEOUT);
    }
    else
    {
      error_msg = "Failed to get Authentication vector";
      LOG_DEBUG(error_msg.c_str());
      tdata->msg->line.status.code = PJSIP_SC_FORBIDDEN;
      tdata->msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_FORBIDDEN);
    }

    SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_FAILED, 0);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    pjsip_tx_data_invalidate_msg(tdata);
  }
}