/*!
 * \internal
 * \brief Adds diversion header information to an outbound SIP message
 *
 * \param tdata The outbound message
 * \param data The redirecting data used to fill parts of the diversion header
 */
static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirecting *data)
{
	pjsip_fromto_hdr *hdr;
	pjsip_name_addr *name_addr;
	pjsip_sip_uri *uri;
	pjsip_param *param;
	pjsip_fromto_hdr *old_hdr;
	const char *reason_str;
	const char *quote_str;
	char *reason_buf;

	struct ast_party_id *id = &data->from;
	pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;

	if (!id->number.valid || ast_strlen_zero(id->number.str)) {
		return;
	}

	hdr = pjsip_from_hdr_create(tdata->pool);
	hdr->type = PJSIP_H_OTHER;
	pj_strdup(tdata->pool, &hdr->name, &diversion_name);
	hdr->sname = hdr->name;

	name_addr = pjsip_uri_clone(tdata->pool, base);
	uri = pjsip_uri_get_uri(name_addr->uri);

	pj_strdup2(tdata->pool, &name_addr->display, id->name.str);
	pj_strdup2(tdata->pool, &uri->user, id->number.str);

	param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
	param->name = pj_str("reason");

	reason_str = reason_code_to_str(&data->reason);

	/* Reason is either already quoted or it is a token to not need quotes added. */
	quote_str = *reason_str == '\"' || sip_is_token(reason_str) ? "" : "\"";

	reason_buf = pj_pool_alloc(tdata->pool, strlen(reason_str) + 3);
	sprintf(reason_buf, "%s%s%s", quote_str, reason_str, quote_str);/* Safe */

	param->value = pj_str(reason_buf);

	pj_list_insert_before(&hdr->other_param, param);

	hdr->uri = (pjsip_uri *) name_addr;
	old_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &diversion_name, NULL);
	if (old_hdr) {
		pj_list_erase(old_hdr);
	}
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
}
void SessionExpiresHelper::process_request(pjsip_msg* req,
                                           pj_pool_t* pool,
                                           SAS::TrailId trail)
{
  // Session expires is only allowed on INVITE and UPDATE methods.
  pjsip_method* method = &req->line.req.method;

  if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) &&
      (pjsip_method_cmp(method, &METHOD_UPDATE) != 0))
  {
    return;
  }

  // Store if this is the initial transaction on a dialog, and if the UAC
  // supports session timers. We need both of these when processing the
  // response.
  _initial_request = (PJSIP_MSG_TO_HDR(req)->tag.slen == 0);
  _uac_supports_timer = timer_supported(req);

  // Find the session-expires header (if present) and the minimum
  // session-expires. Note that the latter has a default value.
  pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*)
    pjsip_msg_find_hdr_by_name(req, &STR_SESSION_EXPIRES, NULL);

  pjsip_min_se_hdr* min_se_hdr = (pjsip_min_se_hdr*)
    pjsip_msg_find_hdr_by_name(req, &STR_MIN_SE, NULL);

  SessionInterval min_se = (min_se_hdr != NULL) ?
                            min_se_hdr->expires :
                            DEFAULT_MIN_SE;

  if ((se_hdr != NULL) && (se_hdr->expires < _target_se))
  {
    // The request already has a session expires that is below our target. We
    // don't need to change the value.
    TRC_DEBUG("Session expires already set to %d", se_hdr->expires);
  }
  else
  {
    // No pre-existing session expires, or the current value is greater than
    // our target. Set it to as close to our target as possible, but don't set
    // it below the min-SE.
    if (se_hdr == NULL)
    {
      se_hdr = pjsip_session_expires_hdr_create(pool);
      pjsip_msg_add_hdr(req, (pjsip_hdr*)se_hdr);
    }

    se_hdr->expires = std::max(_target_se, min_se);

    TRC_DEBUG("Set session expires to %d", se_hdr->expires);
  }

  // Make a note of the session expires (we may need it when processing the
  // response)
  _se_on_req = se_hdr->expires;
}
Exemple #3
0
/**
 * Get Q.850 reason code from pjsip_event
 */
int get_q850_reason_code(pjsip_event *e) {
	int cause = 0;
	const pj_str_t HDR = { "Reason", 6 };
	pj_bool_t is_q850 = PJ_FALSE;

	if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {

		pjsip_generic_string_hdr *hdr =
				(pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(
						e->body.tsx_state.src.rdata->msg_info.msg, &HDR, NULL);

		if (hdr) {
			char *token = strtok(hdr->hvalue.ptr, ";");
			while (token != NULL) {
				if (!is_q850 && pj_ansi_stricmp(token, "Q.850") == 0) {
					is_q850 = PJ_TRUE;
				} else if (cause == 0) {
					cause = lookup_q850_cause(token);
				}
				token = strtok(NULL, ";");
			}
		}
	}

	return (is_q850) ? cause : 0;
}
Exemple #4
0
static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
	const pj_str_t str_reason = { "Reason", 6 };
	pjsip_generic_string_hdr *header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_reason, NULL);
	char buf[20], *cause, *text;
	int code;

	if (!header) {
		return;
	}

	ast_copy_pj_str(buf, &header->hvalue, sizeof(buf));
	cause = ast_skip_blanks(buf);

	if (strncasecmp(cause, "Q.850", 5) || !(cause = strstr(cause, "cause="))) {
		return;
	}

	/* If text is present get rid of it */
	if ((text = strstr(cause, ";"))) {
		*text = '\0';
	}

	if (sscanf(cause, "cause=%30d", &code) != 1) {
		return;
	}

	ast_channel_hangupcause_set(session->channel, code & 0x7f);
}
/*!
 * \internal
 * \brief Add a Remote-Party-ID header to an outbound message
 * \param tdata The message to add the header to
 * \param id The identification information used to populate the header
 */
static void add_rpid_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
{
	static const pj_str_t pj_rpid_name = { "Remote-Party-ID", 15 };
	pjsip_fromto_hdr *rpid_hdr;
	pjsip_fromto_hdr *old_rpid;

	if (!id->number.valid) {
		return;
	}

	/* Since inv_session reuses responses, we have to make sure there's not already
	 * a P-Asserted-Identity present. If there is, we just modify the old one.
	 */
	old_rpid = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_rpid_name, NULL);
	if (old_rpid) {
		modify_id_header(tdata->pool, old_rpid, id);
		add_privacy_params(tdata, old_rpid, id);
		return;
	}

	rpid_hdr = create_new_id_hdr(&pj_rpid_name, tdata, id);
	if (!rpid_hdr) {
		return;
	}
	add_privacy_params(tdata, rpid_hdr, id);
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)rpid_hdr);
}
/*!
 * \internal
 * \brief Set an ast_party_id structure based on data in a P-Asserted-Identity header
 *
 * This makes use of \ref set_id_from_hdr for setting name and number. It uses
 * the contents of a Privacy header in order to set presentation information.
 *
 * \param rdata The incoming message
 * \param[out] id The ID to set
 * \retval 0 Successfully set the party ID
 * \retval non-zero Could not set the party ID
 */
static int set_id_from_pai(pjsip_rx_data *rdata, struct ast_party_id *id)
{
	static const pj_str_t pai_str = { "P-Asserted-Identity", 19 };
	static const pj_str_t privacy_str = { "Privacy", 7 };
	pjsip_fromto_hdr *pai_hdr = get_id_header(rdata, &pai_str);
	pjsip_generic_string_hdr *privacy;

	if (!pai_hdr) {
		return -1;
	}

	set_id_from_hdr(pai_hdr, id);

	if (!id->number.valid) {
		return -1;
	}

	privacy = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &privacy_str, NULL);
	if (privacy && !pj_stricmp2(&privacy->hvalue, "id")) {
		id->number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
		id->name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
	} else {
		id->number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
		id->name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
	}

	return 0;
}
/**
 * VoX Mobile :: negative SIP response handler
 */
int get_vox_error_info(pjsip_event *e)
{
    int cause = 0;
    const pj_str_t HDR = { "Error-Info", 10 };
    const pj_str_t BLOCKED_PSTN = { "PSTN not allowed", 16 };
    const pj_str_t BLOCKED_DESTINATION = { "Blocked destination", 19 };

    if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {

        pjsip_generic_string_hdr *hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(
                                         e->body.tsx_state.src.rdata->msg_info.msg,
                                         &HDR,
                                         NULL);

        if (hdr) {
            if (pj_strcmp(&hdr->hvalue, &BLOCKED_PSTN) == 0)
                cause = 403;
            else if (pj_strcmp(&hdr->hvalue, &BLOCKED_DESTINATION) == 0)
                cause = 580;
            else {
                cause = (int)pj_strtoul(&hdr->hvalue);
            }
        }
    }

    return cause;
}
/**
 * Get TAG reason code from pjsip_event
 */
int get_reason_code(pjsip_event *e, char* tag, int (*code_parser)(const char*)) {
	int code = 0;
	const pj_str_t HDR = { "Reason", 6 };
	pj_bool_t has_tag = PJ_FALSE;

	if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {

		pjsip_generic_string_hdr *hdr =
				(pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(
						e->body.tsx_state.src.rdata->msg_info.msg, &HDR, NULL);

		// TODO : check if the header should not be parsed here? -- I don't see how it could work without parsing.
		if (hdr) {
	        char *reason_header = strdup(hdr->hvalue.ptr);
			char *token = strtok(reason_header, ";");
			while (token != NULL) {
				if (!has_tag && pj_ansi_stricmp(token, tag) == 0) {
				    has_tag = PJ_TRUE;
				} else if (code == 0) {
				    code = code_parser(token);
				}
				token = strtok(NULL, ";");
			}
			free(reason_header);
		}
	}

	return (has_tag) ? code : 0;
}
Exemple #9
0
PJ_DECL(pj_str_t) get_rx_data_header(const pj_str_t name, pjsip_rx_data* data){
	pjsip_generic_string_hdr *hdr =
			(pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(data->msg_info.msg, &name, NULL);
	if (hdr && hdr->hvalue.ptr) {
		return hdr->hvalue;
	}
	return pj_str("");
}
Exemple #10
0
/// Remove all existing copies of a header.  The header to delete must
/// not be one that has an abbreviation.
void PJUtils::remove_hdr(pjsip_msg* msg,
                         const pj_str_t* name)
{
  while (pjsip_hdr* hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, name, NULL))
  {
    pj_list_erase(hdr);
  }
}
static int has_call_feature(pjsip_rx_data *rdata)
{
	static const pj_str_t call_feature_str = { "X-Digium-Call-Feature", 21 };

	pjsip_generic_string_hdr *hdr = pjsip_msg_find_hdr_by_name(
		rdata->msg_info.msg, &call_feature_str, NULL);

	return hdr && !pj_stricmp2(&hdr->hvalue, SEND_TO_VM_HEADER_VALUE);
}
Exemple #12
0
/// Determine the served user for originating requests.
pjsip_uri* PJUtils::orig_served_user(pjsip_msg* msg)
{
  // The served user for originating requests is determined from the
  // P-Served-User or P-Asserted-Identity headers.  For extra compatibility,
  // we will also look at the From header if neither of the IMS headers is
  // present.
  pjsip_uri* uri = NULL;
  pjsip_routing_hdr* served_user = (pjsip_routing_hdr*)
                     pjsip_msg_find_hdr_by_name(msg, &STR_P_SERVED_USER, NULL);

  if (served_user != NULL)
  {
    uri = (pjsip_uri*)pjsip_uri_get_uri(&served_user->name_addr);
    LOG_DEBUG("Served user from P-Served-User header");
  }

  if (uri == NULL)
  {
    // No P-Served-User header present, so check for P-Asserted-Identity
    // header.
    pjsip_routing_hdr* asserted_id = (pjsip_routing_hdr*)
               pjsip_msg_find_hdr_by_name(msg, &STR_P_ASSERTED_IDENTITY, NULL);

    if (asserted_id != NULL)
    {
      uri = (pjsip_uri*)pjsip_uri_get_uri(&asserted_id->name_addr);
      LOG_DEBUG("Served user from P-Asserted-Identity header");
    }
  }

  if (uri == NULL)
  {
    // Neither IMS header is present, so use the From header.  This isn't
    // strictly speaking IMS compliant.
    LOG_DEBUG("From header %p", PJSIP_MSG_FROM_HDR(msg));
    uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_FROM_HDR(msg)->uri);
    LOG_DEBUG("Served user from From header (%p)", uri);
  }

  return uri;
}
static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
{
	pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, NULL);

	if (!path_hdr) {
		return 0;
	}

	*path_str = ast_str_create(64);
	if (!path_str) {
		return -1;
	}

	ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);

	while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
		ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
	}

	return 0;
}
Exemple #14
0
void PJUtils::clone_header(const pj_str_t* hdr_name, pjsip_msg* old_msg, pjsip_msg* new_msg, pj_pool_t* pool)
{
  pjsip_hdr* original_hdr = NULL;
  pjsip_hdr* last_hdr = NULL;
  while ((original_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(old_msg, hdr_name, original_hdr)) && (last_hdr != original_hdr))
  {
    LOG_INFO("Cloning header! %ld", (long int)original_hdr);
    pjsip_hdr* new_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, original_hdr);
    pjsip_msg_add_hdr(new_msg, new_hdr);
    last_hdr = original_hdr;
  }
}
Exemple #15
0
/// Delete all existing copies of a header.  The header to delete must
/// not be one that has an abbreviation.
void PJUtils::delete_header(pjsip_msg* msg,
                            const pj_str_t* name)
{
  while (1)
  {
    pjsip_hdr* hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, name, NULL);
    if (hdr)
    {
      pj_list_erase(hdr);
    }
    else
    {
      break;
    }
  }
}
static pjsip_fromto_hdr *get_diversion_header(pjsip_rx_data *rdata)
{
	static const pj_str_t from_name = { "From", 4 };

	pjsip_generic_string_hdr *hdr;
	pj_str_t value;
	int size;

	if (!(hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &diversion_name, NULL))) {
		return NULL;
	}

	pj_strdup_with_null(rdata->tp_info.pool, &value, &hdr->hvalue);

	/* parse as a fromto header */
	return pjsip_parse_hdr(rdata->tp_info.pool, &from_name, value.ptr,
			       pj_strlen(&value), &size);
}
/*!
 * \internal
 * \brief Add a Privacy header to an outbound message
 *
 * When sending a P-Asserted-Identity header, if privacy is requested, then we
 * will need to indicate such by adding a Privacy header. Similarly, if no
 * privacy is requested, and a Privacy header already exists on the message,
 * then the old Privacy header should be removed.
 *
 * \param tdata The outbound message to add the Privacy header to
 * \param id The id information used to determine privacy
 */
static void add_privacy_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
{
	static const pj_str_t pj_privacy_name = { "Privacy", 7 };
	static const pj_str_t pj_privacy_value = { "id", 2 };
	pjsip_hdr *old_privacy;

	old_privacy = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_privacy_name, NULL);

	if ((ast_party_id_presentation(id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
		if (old_privacy) {
			pj_list_erase(old_privacy);
		}
	} else if (!old_privacy) {
		pjsip_generic_string_hdr *privacy_hdr = pjsip_generic_string_hdr_create(
				tdata->pool, &pj_privacy_name, &pj_privacy_value);
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)privacy_hdr);
	}
}
Exemple #18
0
std::string
fetchHeaderValue(pjsip_msg *msg, const std::string &field)
{
    pj_str_t name = pj_str((char*) field.c_str());
    pjsip_generic_string_hdr *hdr = static_cast<pjsip_generic_string_hdr*>(pjsip_msg_find_hdr_by_name(msg, &name, NULL));

    if (!hdr)
        return "";

    std::string value(hdr->hvalue.ptr, hdr->hvalue.slen);

    size_t pos = value.find("\n");

    if (pos != std::string::npos)
        return value.substr(0, pos);
    else
        return "";
}
Exemple #19
0
/// Delete all existing copies of a header and replace with a new one.
void PJUtils::set_generic_header(pjsip_tx_data* tdata,
                                 const pj_str_t* name,
                                 const pj_str_t* value)
{
  while (1)
  {
    pjsip_hdr* hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(tdata->msg, name, NULL);
    if (hdr)
    {
      pj_list_erase(hdr);
    }
    else
    {
      break;
    }
  }

  pjsip_generic_string_hdr* new_hdr = pjsip_generic_string_hdr_create(tdata->pool, name, value);
  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_hdr);
}
/*!
 * \internal
 * \brief Add a P-Asserted-Identity header to an outbound message
 * \param tdata The message to add the header to
 * \param id The identification information used to populate the header
 */
static void add_pai_header(const struct ast_sip_session *session, pjsip_tx_data *tdata, const struct ast_party_id *id)
{
	static const pj_str_t pj_pai_name = { "P-Asserted-Identity", 19 };
	pjsip_fromto_hdr *base;
	pjsip_fromto_hdr *pai_hdr;
	pjsip_fromto_hdr *old_pai;

	/* Since inv_session reuses responses, we have to make sure there's not already
	 * a P-Asserted-Identity present. If there is, we just modify the old one.
	 */
	old_pai = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_pai_name, NULL);
	if (old_pai) {
		/* If type is OTHER, then the existing header was most likely
		 * added by the PJSIP_HEADER dial plan function as a simple
		 * name/value pair.  We can't pass this to modify_id_header because
		 * there are no virtual functions to get the uri.  We could parse
		 * it into a pjsip_fromto_hdr but it isn't worth it since
		 * modify_id_header is just going to overwrite the name and number
		 * anyway.  We'll just remove it from the header list instead
		 * and create a new one.
		 */
		if (old_pai->type == PJSIP_H_OTHER) {
			pj_list_erase(old_pai);
		} else {
			ast_sip_modify_id_header(tdata->pool, old_pai, id);
			add_privacy_header(tdata, id);
			return;
		}
	}

	base = tdata->msg->type == PJSIP_REQUEST_MSG ? session->saved_from_hdr :
		PJSIP_MSG_TO_HDR(tdata->msg);

	pai_hdr = create_new_id_hdr(&pj_pai_name, base, tdata, id);
	if (!pai_hdr) {
		return;
	}
	add_privacy_header(tdata, id);

	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr);
}
/*!
 * \internal
 * \brief Adds diversion header information to an outbound SIP message
 *
 * \param tdata The outbound message
 * \param data The redirecting data used to fill parts of the diversion header
 */
static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirecting *data)
{
	pjsip_fromto_hdr *hdr;
	pjsip_name_addr *name_addr;
	pjsip_sip_uri *uri;
	pjsip_param *param;
	pjsip_fromto_hdr *old_hdr;

	struct ast_party_id *id = &data->from;
	pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;

	if (!id->number.valid || ast_strlen_zero(id->number.str)) {
		return;
	}

	hdr = pjsip_from_hdr_create(tdata->pool);
	hdr->type = PJSIP_H_OTHER;
	pj_strdup(tdata->pool, &hdr->name, &diversion_name);
	hdr->sname.slen = 0;

	name_addr = pjsip_uri_clone(tdata->pool, base);
	uri = pjsip_uri_get_uri(name_addr->uri);

	pj_strdup2(tdata->pool, &name_addr->display, id->name.str);
	pj_strdup2(tdata->pool, &uri->user, id->number.str);

	param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
	param->name = pj_str("reason");
	param->value = pj_str((char*)reason_code_to_str(&data->reason));
	pj_list_insert_before(&hdr->other_param, param);

	hdr->uri = (pjsip_uri *) name_addr;
	old_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &diversion_name, NULL);
	if (old_hdr) {
		pj_list_erase(old_hdr);
	}
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
}
static void diversion_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
	static const pj_str_t contact_name = { "Contact", 7 };

	pjsip_status_line status = rdata->msg_info.msg->line.status;
	pjsip_fromto_hdr *div_hdr;
	pjsip_contact_hdr *contact_hdr;

	if ((status.code != 302) && (status.code != 181)) {
		return;
	}

	/* use the diversion header info if there is one. if not one then use the
           session caller id info. if that doesn't exist use info from the To hdr*/
	if (!(div_hdr = get_diversion_header(rdata)) && !session->id.number.valid) {
		div_hdr = PJSIP_MSG_TO_HDR(rdata->msg_info.msg);
	}

	contact_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &contact_name, NULL);

	set_redirecting(session, div_hdr, contact_hdr ?	(pjsip_name_addr*)contact_hdr->uri :
			(pjsip_name_addr*)PJSIP_MSG_FROM_HDR(rdata->msg_info.msg)->uri);
}
/// Add P-Charging headers on incoming out-of-dialog/dialog initiating requests
static void proxy_add_p_charging_header(pjsip_tx_data *tdata)
{
  LOG_DEBUG("Add P-Charging headers");

  std::string cdf_domain = PJUtils::pj_str_to_string(&stack_data.cdf_domain);

  if (cdf_domain != "")
  {
    // Add the P-Charging-Function-Addresses. The value of the CDF is passed in
    // as a parameter in bono - if this isn't present then don't set these
    // headers.
    pjsip_p_c_f_a_hdr* p_c_f_a = pjsip_p_c_f_a_hdr_create(tdata->pool);
    pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param));
    new_param->name = STR_CCF;
    new_param->value = stack_data.cdf_domain;

    pj_list_insert_before(&p_c_f_a->ccf, new_param);
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)p_c_f_a);

    // Add the P-Charging-Vector Id. The icid-value is the Call-ID, and the
    // icid-generated-at is the bono hostname - it must be unique to the node that
    // generates it.
    pjsip_cid_hdr* call_id = (pjsip_cid_hdr*)pjsip_msg_find_hdr_by_name(tdata->msg,
                                                                        &STR_CALL_ID,
                                                                        NULL);
    std::string c_id = PJUtils::pj_str_to_string(&call_id->id);
    c_id.erase(std::remove(c_id.begin(), c_id.end(), '@'), c_id.end());
    c_id.erase(std::remove(c_id.begin(), c_id.end(), '"'), c_id.end());

    pjsip_p_c_v_hdr* p_c_v = pjsip_p_c_v_hdr_create(tdata->pool);

    pj_strdup2(tdata->pool, &p_c_v->icid, c_id.c_str());
    p_c_v->icid_gen_addr = stack_data.public_host;

    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)p_c_v);
  }
}
/*!
 * \internal
 * \brief Get a P-Asserted-Identity or Remote-Party-ID header from an incoming message
 *
 * This function will parse the header as if it were a From header. This allows for us
 * to easily manipulate the URI, as well as add, modify, or remove parameters from the
 * header
 *
 * \param rdata The incoming message
 * \param header_name The name of the ID header to find
 * \retval NULL No ID header present or unable to parse ID header
 * \retval non-NULL The parsed ID header
 */
static pjsip_fromto_hdr *get_id_header(pjsip_rx_data *rdata, const pj_str_t *header_name)
{
	static const pj_str_t from = { "From", 4 };
	pj_str_t header_content;
	pjsip_fromto_hdr *parsed_hdr;
	pjsip_generic_string_hdr *ident = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
			header_name, NULL);
	int parsed_len;

	if (!ident) {
		return NULL;
	}

	pj_strdup_with_null(rdata->tp_info.pool, &header_content, &ident->hvalue);

	parsed_hdr = pjsip_parse_hdr(rdata->tp_info.pool, &from, header_content.ptr,
			pj_strlen(&header_content), &parsed_len);

	if (!parsed_hdr) {
		return NULL;
	}

	return parsed_hdr;
}
/*!
 * \internal
 * \brief Add a Remote-Party-ID header to an outbound message
 * \param tdata The message to add the header to
 * \param id The identification information used to populate the header
 */
static void add_rpid_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
{
	static const pj_str_t pj_rpid_name = { "Remote-Party-ID", 15 };
	pjsip_fromto_hdr *rpid_hdr;
	pjsip_fromto_hdr *old_rpid;

	/* Since inv_session reuses responses, we have to make sure there's not already
	 * a P-Asserted-Identity present. If there is, we just modify the old one.
	 */
	old_rpid = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_rpid_name, NULL);
	if (old_rpid) {
		/* If type is OTHER, then the existing header was most likely
		 * added by the PJSIP_HEADER dial plan function as a simple
		 * name/value pair.  We can't pass this to modify_id_header because
		 * there are no virtual functions to get the uri.  We could parse
		 * it into a pjsip_fromto_hdr but it isn't worth it since
		 * modify_id_header is just going to overwrite the name and number
		 * anyway.  We'll just remove it from the header list instead
		 * and create a new one.
		 */
		if (old_rpid->type == PJSIP_H_OTHER) {
			pj_list_erase(old_rpid);
		} else {
			modify_id_header(tdata->pool, old_rpid, id);
			add_privacy_params(tdata, old_rpid, id);
			return;
		}
	}

	rpid_hdr = create_new_id_hdr(&pj_rpid_name, tdata, id);
	if (!rpid_hdr) {
		return;
	}
	add_privacy_params(tdata, rpid_hdr, id);
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)rpid_hdr);
}
/*!
 * \internal
 * \brief Add a P-Asserted-Identity header to an outbound message
 * \param tdata The message to add the header to
 * \param id The identification information used to populate the header
 */
static void add_pai_header(pjsip_tx_data *tdata, const struct ast_party_id *id)
{
	static const pj_str_t pj_pai_name = { "P-Asserted-Identity", 19 };
	pjsip_fromto_hdr *pai_hdr;
	pjsip_fromto_hdr *old_pai;

	/* Since inv_session reuses responses, we have to make sure there's not already
	 * a P-Asserted-Identity present. If there is, we just modify the old one.
	 */
	old_pai = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_pai_name, NULL);
	if (old_pai) {
		modify_id_header(tdata->pool, old_pai, id);
		add_privacy_header(tdata, id);
		return;
	}

	pai_hdr = create_new_id_hdr(&pj_pai_name, tdata, id);
	if (!pai_hdr) {
		return;
	}
	add_privacy_header(tdata, id);

	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr);
}
Exemple #27
0
//////////////////////////////////////////////////////////////////////////
// Request handler to receive out-of-dialog NOTIFY (from Asterisk)
static pj_bool_t on_rx_request(pjsip_rx_data *rdata)
{
	if (strstr(pj_strbuf(&rdata->msg_info.msg->line.req.method.name),
		"NOTIFY"))
	{
		pjsip_generic_string_hdr * hdr;
		pj_str_t did_str = pj_str("Event");
		hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &did_str, NULL);
		if (!hdr) return false;

		// We have an event header, now determine if it's contents are "message-summary"
		if (pj_strcmp2(&hdr->hvalue, "message-summary")) return false;

		pjsip_msg_body * body_p = rdata->msg_info.msg->body;

		wchar_t* buf = (wchar_t*)pj_pool_alloc(app_config.pool, body_p->len);
		buf = PJ_STRING_TO_NATIVE((char*)body_p->data, buf, body_p->len);

		// Process body message as desired...
		if (strncmp((char*)body_p->data, "Messages-Waiting: yes", body_p->len) != 0)
		{
			if (cb_mwi != 0) cb_mwi(1, buf);
		}
		else
		{
			if (cb_mwi != 0) cb_mwi(0, buf);
		}
		PJ_LOG(3,(THIS_FILE,"MWI message: %s", buf));
	}

	pjsip_endpt_respond_stateless(pjsip_ua_get_endpt(pjsip_ua_instance()),
		rdata, 200, NULL,
		NULL, NULL);

	return PJ_TRUE;
}
Exemple #28
0
// Reject request unless it's a SUBSCRIBE targeted at the home domain / this node.
pj_bool_t subscription_on_rx_request(pjsip_rx_data *rdata)
{
  SAS::TrailId trail = get_trail(rdata);

  if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port)
  {
    // Not an S-CSCF, so don't handle SUBSCRIBEs.
    return PJ_FALSE; // LCOV_EXCL_LINE
  }

  if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method()))
  {
    // This isn't a SUBSCRIBE, so this module can't process it.
    return PJ_FALSE;
  }

  if (!((PJUtils::is_home_domain(rdata->msg_info.msg->line.req.uri) ||
         (PJUtils::is_uri_local(rdata->msg_info.msg->line.req.uri))) &&
        PJUtils::check_route_headers(rdata)))
  {
    LOG_DEBUG("Rejecting subscription request not targeted at this domain or node");
    SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0);
    SAS::report_event(event);
    return PJ_FALSE;
  }

  // SUBSCRIBE request targeted at the home domain or specifically at this node. Check
  // whether it should be processed by this module or passed up to an AS.
  pjsip_msg *msg = rdata->msg_info.msg;

  // A valid subscription must have the Event header set to "reg". This is case-sensitive
  pj_str_t event_name = pj_str("Event");
  pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(msg, &event_name, NULL);

  if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg"))
  {
    // The Event header is missing or doesn't match "reg"
    LOG_DEBUG("Rejecting subscription request with invalid event header");

    SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0);
    if (event)
    {
      char event_hdr_str[256];
      memset(event_hdr_str, 0, 256);
      pjsip_hdr_print_on(event, event_hdr_str, 255);
      sas_event.add_var_param(event_hdr_str);
    }
    SAS::report_event(sas_event);

    return PJ_FALSE;
  }

  // Accept header may be present - if so must include the application/reginfo+xml
  pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ACCEPT, NULL);
  if (accept)
  {
    bool found = false;
    pj_str_t reginfo = pj_str("application/reginfo+xml");
    for (uint32_t i = 0; i < accept->count; i++)
    {
      if (!pj_strcmp(accept->values + i, &reginfo))
      {
        found = true;
      }
    }

    if (!found)
    {
      // The Accept header (if it exists) doesn't contain "application/reginfo+xml"
      LOG_DEBUG("Rejecting subscription request with invalid accept header");
      char accept_hdr_str[256];
      memset(accept_hdr_str, 0, 256);
      pjsip_hdr_print_on(accept, accept_hdr_str, 255);
      SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0);
      event.add_var_param(accept_hdr_str);
      SAS::report_event(event);

      return PJ_FALSE;
    }
  }

  process_subscription_request(rdata);
  return PJ_TRUE;
}
Exemple #29
0
void process_register_request(pjsip_rx_data* rdata)
{
  pj_status_t status;
  int st_code = PJSIP_SC_OK;

  // 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))
  {
    // Reject a non-SIP/SIPS 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).
    // LCOV_EXCL_START
    LOG_ERROR("Rejecting register request using non SIP URI");
    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_NOT_FOUND,
                               NULL,
                               NULL,
                               NULL);
    return;
    // LCOV_EXCL_STOP
  }

  // Canonicalize the public ID from the URI in the To header.
  std::string public_id = PJUtils::aor_from_uri((pjsip_sip_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);;
  int cseq = rdata->msg_info.cseq->cseq;
  pjsip_msg *msg = rdata->msg_info.msg;

  // Add SAS markers to the trail attached to the message so the trail
  // becomes searchable.
  SAS::TrailId trail = get_trail(rdata);
  LOG_DEBUG("Report SAS start marker - trail (%llx)", trail);
  SAS::Marker start_marker(trail, SASMarker::INIT_TIME, 1u);
  SAS::report_marker(start_marker);

  SAS::Marker calling_dn(trail, SASMarker::CALLING_DN, 1u);
  pjsip_sip_uri* calling_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri);
  calling_dn.add_var_param(calling_uri->user.slen, calling_uri->user.ptr);
  SAS::report_marker(calling_dn);

  SAS::Marker cid_marker(trail, SASMarker::SIP_CALL_ID, 1u);
  cid_marker.add_var_param(rdata->msg_info.cid->id.slen, rdata->msg_info.cid->id.ptr);
  SAS::report_marker(cid_marker, SAS::Marker::Scope::TrailGroup);

  // Query the HSS for the associated URIs.
  // This should really include the private ID, but we don't yet have a
  // homestead API for it.  Homestead won't be able to query a third-party HSS
  // without the private ID.
  Json::Value* uris = hss->get_associated_uris(public_id, trail);
  if ((uris == NULL) ||
      (uris->size() == 0))
  {
    // 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.  Reject with 403.
    LOG_ERROR("Rejecting register request with invalid public/private identity");
    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_FORBIDDEN,
                               NULL,
                               NULL,
                               NULL);
    return;
  }

  // Determine the AOR from the first entry in the uris array.
  std::string aor = uris->get((Json::ArrayIndex)0, Json::Value::null).asString();
  LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str());

  // Find the expire headers in the message.
  pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);

  // Get the system time in seconds for calculating absolute expiry times.
  int now = time(NULL);
  int expiry = 0;

  // The registration service uses optimistic locking to avoid concurrent
  // updates to the same AoR conflicting.  This means we have to loop
  // reading, updating and writing the AoR until the write is successful.
  RegData::AoR* aor_data = NULL;
  do
  {
    if (aor_data != NULL)
    {
      delete aor_data; // LCOV_EXCL_LINE - Single-threaded tests mean we'll
                       //                  always pass CAS.
    }

    // Find the current bindings for the AoR.
    aor_data = store->get_aor_data(aor);
    LOG_DEBUG("Retrieved AoR data %p", aor_data);

    if (aor_data == NULL)
    {
      // Failed to get data for the AoR because there is no connection
      // to the store.  Reject the register with a 500 response.
      // LCOV_EXCL_START - local store (used in testing) never fails
      LOG_ERROR("Failed to get AoR binding for %s from store", aor.c_str());
      st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
      break;
      // LCOV_EXCL_STOP
    }

    // Now loop through all the contacts.  If there are multiple contacts in
    // the contact header in the SIP message, pjsip parses them to separate
    // contact header structures.
    pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
    while (contact != NULL)
    {
      if (contact->star)
      {
        // Wildcard contact, which can only be used to clear all bindings for
        // the AoR.
        aor_data->clear();
        break;
      }

      pjsip_uri* uri = (contact->uri != NULL) ?
                           (pjsip_uri*)pjsip_uri_get_uri(contact->uri) :
                           NULL;

      if ((uri != NULL) &&
          (PJSIP_URI_SCHEME_IS_SIP(uri)))
      {
        // The binding identifier is based on the +sip.instance parameter if
        // it is present.  If not the contact URI is used instead.
        std::string contact_uri = PJUtils::uri_to_string(PJSIP_URI_IN_CONTACT_HDR, uri);
        std::string binding_id = get_binding_id(contact);
        if (binding_id == "")
        {
          binding_id = contact_uri;
        }
        LOG_DEBUG(". Binding identifier for contact = %s", binding_id.c_str());

        // Find the appropriate binding in the bindings list for this AoR.
        RegData::AoR::Binding* binding = aor_data->get_binding(binding_id);

        if ((cid != binding->_cid) ||
            (cseq > binding->_cseq))
        {
          // Either this is a new binding, has come from a restarted device, or
          // is an update to an existing binding.
          binding->_uri = contact_uri;

          // TODO Examine Via header to see if we're the first hop
          // TODO Only if we're not the first hop, check that the top path header has "ob" parameter

          // Get the Path headers, if present.  RFC 3327 allows us the option of
          // rejecting a request with a Path header if there is no corresponding
          // "path" entry in the Supported header but we don't do so on the assumption
          // that the edge proxy knows what it's doing.
          binding->_path_headers.clear();
          pjsip_generic_string_hdr* path_hdr =
            (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL);
          while (path_hdr)
          {
            std::string path = PJUtils::pj_str_to_string(&path_hdr->hvalue);
            LOG_DEBUG("Path header %s", path.c_str());

            // Extract all the paths from this header.
            Utils::split_string(path, ',', binding->_path_headers, 0, true);

            // Look for the next header.
            path_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next);
          }

          binding->_cid = cid;
          binding->_cseq = cseq;
          binding->_priority = contact->q1000;
          binding->_params.clear();
          pjsip_param* p = contact->other_param.next;
          while ((p != NULL) && (p != &contact->other_param))
          {
            std::string pname = PJUtils::pj_str_to_string(&p->name);
            std::string pvalue = PJUtils::pj_str_to_string(&p->value);
            binding->_params.push_back(std::make_pair(pname, pvalue));
            p = p->next;
          }

          // Calculate the expiry period for the updated binding.
          expiry = (contact->expires != -1) ? contact->expires :
                       (expires != NULL) ? expires->ivalue : 300;
          if (expiry > 300)
          {
            // Expiry is too long, set it to the maximum of 300 seconds (5 minutes).
            expiry = 300;
          }

          binding->_expires = now + expiry;

          if (analytics != NULL)
          {
            // Generate an analytics log for this binding update.
            analytics->registration(aor, binding_id, contact_uri, expiry);
          }
        }
      }
      contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, contact->next);
    }
  }
  while (!store->set_aor_data(aor, aor_data));

  if (aor_data != NULL)
  {
    // Log the bindings.
    log_bindings(aor, aor_data);
  }

  // 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 REGISTER %d response %s", st_code,
              PJUtils::pj_status_to_string(status).c_str());
    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_INTERNAL_SERVER_ERROR,
                               NULL,
                               NULL,
                               NULL);
    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);
    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");
    tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR;
    status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL);
    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 (RegData::AoR::Bindings::const_iterator i = aor_data->bindings().begin();
       i != aor_data->bindings().end();
       ++i)
  {
    RegData::AoR::Binding* binding = i->second;
    if (binding->_expires > now)
    {
      // The binding hasn't expired.
      pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool);
      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::list<std::pair<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);
        }
        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());
        // LCOV_EXCL_STOP
      }
    }
  }

  // Deal with path header related fields in the response.
  pjsip_generic_string_hdr* path_hdr =
    (pjsip_generic_string_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_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next);
  }

  // Construct a Service-Route header pointing at the sprout cluster.  We don't
  // care which sprout handles the subsequent requests as they all have access
  // to all subscriber information.
  pjsip_sip_uri* service_route_uri = pjsip_sip_uri_create(tdata->pool, false);
  pj_strdup(tdata->pool,
            &service_route_uri->host,
            &stack_data.sprout_cluster_domain);
  service_route_uri->port = stack_data.trusted_port;
  service_route_uri->transport_param = pj_str("TCP");
  service_route_uri->lr_param = 1;

  pjsip_route_hdr* service_route = pjsip_route_hdr_create(tdata->pool);
  service_route->name = STR_SERVICE_ROUTE;
  service_route->sname = pj_str("");
  service_route->name_addr.uri = (pjsip_uri*)service_route_uri;

  pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)service_route);

  // Add P-Associated-URI headers for all of the associated URIs.
  static const pj_str_t p_associated_uri_hdr_name = pj_str("P-Associated-URI");
  for (Json::ValueIterator it = uris->begin(); it != uris->end(); it++)
  {
    pj_str_t associated_uri = {(char*)(*it).asCString(), strlen((*it).asCString())};
    pjsip_hdr* associated_uri_hdr =
      (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool,
                                                  &p_associated_uri_hdr_name,
                                                  &associated_uri);
    pjsip_msg_add_hdr(tdata->msg, associated_uri_hdr);
  }
  delete uris;

  // 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);

  RegistrationUtils::register_with_application_servers(ifchandler, store, rdata, tdata, "");

  // 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, SASMarker::END_TIME, 1u);
  SAS::report_marker(end_marker);
  delete aor_data;
}
void SessionExpiresHelper::process_response(pjsip_msg* rsp,
                                            pj_pool_t* pool,
                                            SAS::TrailId trail)
{
  // Session expires is only allowed on INVITE and UPDATE methods.
  pjsip_method* method = &PJSIP_MSG_CSEQ_HDR(rsp)->method;

  if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) &&
      (pjsip_method_cmp(method, &METHOD_UPDATE) != 0))
  {
    return;
  }

  // We only need to process successful final responses.
  if (!PJSIP_IS_STATUS_IN_CLASS(rsp->line.status.code, 200))
  {
    return;
  }

  pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*)
    pjsip_msg_find_hdr_by_name(rsp, &STR_SESSION_EXPIRES, NULL);

  if (se_hdr == NULL)
  {
    // There is no session-expires header. This means we are most downstream
    // device that supports session timers, and in particular the UAS does not
    // support them.
    //
    // If the UAC does not support session timers, there's nothing more we can
    // do - session timers will not be used for this dialog.
    //
    // If the UAC *does* support session timers, re-add a session-expires header
    // that instructs the UAC to be the refresher.
    if (_uac_supports_timer)
    {
      se_hdr = pjsip_session_expires_hdr_create(pool);
      pjsip_msg_add_hdr(rsp, (pjsip_hdr*)se_hdr);
      se_hdr->expires = _se_on_req;
      se_hdr->refresher = SESSION_REFRESHER_UAC;

      // Also update (or add) the require header to force the UAC to do session
      // refreshes.
      pjsip_require_hdr* require_hdr = (pjsip_require_hdr*)
        pjsip_msg_find_hdr(rsp, PJSIP_H_REQUIRE, NULL);

      if (require_hdr == NULL)
      {
        require_hdr = (pjsip_require_hdr*)pjsip_require_hdr_create(pool);
        pjsip_msg_add_hdr(rsp, (pjsip_hdr*)require_hdr);
      }

      pj_strdup(pool, &require_hdr->values[require_hdr->count], &STR_TIMER);
      require_hdr->count++;
    }
  }

  if (_initial_request)
  {
    if (se_hdr == NULL)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_NO_UA_SUPPORT, 0);
      SAS::report_event(event);
    }
    else if (se_hdr->expires > _target_se)
    {
      SAS::Event event(trail, SASEvent::SESS_TIMER_INTERVAL_TOO_LONG, 0);
      event.add_static_param(_target_se);
      event.add_static_param(se_hdr->expires);
      SAS::report_event(event);
    }
  }
}