/*! * \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; }
/** * 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; }
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; }
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(""); }
/// 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); }
/// 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; }
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; } }
/// 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); } }
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 ""; }
/// 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); }
////////////////////////////////////////////////////////////////////////// // 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; }
// 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, ®info)) { 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; }
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); } } }