/// Gets the maximum expires value from all contacts in a REGISTER message /// (request or response). int PJUtils::max_expires(pjsip_msg* msg) { int max_expires = 0; // Check for an expires header (this will specify the default expiry for // any contacts that don't specify their own expiry). pjsip_expires_hdr* expires_hdr = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); int default_expires = (expires_hdr != NULL) ? expires_hdr->ivalue : 300; pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); while (contact != NULL) { int expires = (contact->expires != -1) ? contact->expires : default_expires; if (expires > max_expires) { max_expires = expires; } contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, contact->next); } return max_expires; }
/// Add SAS markers for the specified call ID and branch IDs on the message (either may be omitted). void PJUtils::mark_sas_call_branch_ids(const SAS::TrailId trail, pjsip_cid_hdr* cid_hdr, pjsip_msg* msg) { // If we have a call ID, log it. if (cid_hdr != NULL) { SAS::Marker cid_marker(trail, MARKER_ID_SIP_CALL_ID, 1u); cid_marker.add_var_param(cid_hdr->id.slen, cid_hdr->id.ptr); SAS::report_marker(cid_marker, SAS::Marker::Scope::Trace); } // If we have a message, look for branch IDs too. if (msg != NULL) { // First find the top Via header. This was added by us. pjsip_via_hdr* top_via = (pjsip_via_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); // If we found the top header (and we really should have done), log its branch ID. if (top_via != NULL) { { SAS::Marker via_marker(trail, MARKER_ID_VIA_BRANCH_PARAM, 1u); via_marker.add_var_param(top_via->branch_param.slen, top_via->branch_param.ptr); SAS::report_marker(via_marker, SAS::Marker::Scope::Trace); } // Now see if we can find the next Via header and log it if so. This will have been added by // the previous server. This means we'll be able to correlate with its trail. pjsip_via_hdr* second_via = (pjsip_via_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_VIA, top_via); if (second_via != NULL) { SAS::Marker via_marker(trail, MARKER_ID_VIA_BRANCH_PARAM, 2u); via_marker.add_var_param(second_via->branch_param.slen, second_via->branch_param.ptr); SAS::report_marker(via_marker, SAS::Marker::Scope::Trace); } } } }
/* Send request */ static void send_request(const pjsip_method *method, int cseq, const pj_str_t *branch, pj_bool_t with_offer) { pjsip_tx_data *tdata; pj_str_t dummy_sdp_str = { "v=0\r\n" "o=- 3360842071 3360842071 IN IP4 192.168.0.68\r\n" "s=pjmedia\r\n" "c=IN IP4 192.168.0.68\r\n" "t=0 0\r\n" "m=audio 4000 RTP/AVP 0 101\r\n" "a=rtcp:4001 IN IP4 192.168.0.68\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=sendrecv\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-15\r\n", 0 }; pj_status_t status; status = pjsip_dlg_create_request(dlg, method, cseq, &tdata); pj_assert(status == PJ_SUCCESS); if (branch) { pjsip_via_hdr *via; via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); pj_strdup(tdata->pool, &via->branch_param, branch); } if (with_offer) { pjsip_msg_body *body; pj_str_t mime_application = { "application", 11}; pj_str_t mime_sdp = {"sdp", 3}; dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr); body = pjsip_msg_body_create(tdata->pool, &mime_application, &mime_sdp, &dummy_sdp_str); tdata->msg->body = body; } status = pjsip_dlg_send_request(dlg, tdata, -1, NULL); pj_assert(status == PJ_SUCCESS); }
/* * A simple registrar, invoked by default_mod_on_rx_request() */ static void simple_registrar(pjsip_rx_data *rdata) { pjsip_tx_data *tdata; const pjsip_expires_hdr *exp; const pjsip_hdr *h; unsigned cnt = 0; pjsip_generic_string_hdr *srv; pj_status_t status; status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(), rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) return; exp = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); h = rdata->msg_info.msg->hdr.next; while (h != &rdata->msg_info.msg->hdr) { if (h->type == PJSIP_H_CONTACT) { const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h; int e = c->expires; if (e < 0) { if (exp) e = exp->ivalue; else e = 3600; } if (e > 0) { pjsip_contact_hdr *nc = (pjsip_contact_hdr*) pjsip_hdr_clone(tdata->pool, h); nc->expires = e; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc); ++cnt; } } h = h->next; } srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL); srv->name = pj_str((char*)"Server"); srv->hvalue = pj_str((char*)"pjsua simple registrar"); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv); pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata, NULL, NULL); }
static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request) { pjsip_auth_clt_sess auth_sess; pjsip_cseq_hdr *cseq; if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(), old_request->pool, 0) != PJ_SUCCESS) { ast_log(LOG_WARNING, "Failed to initialize client authentication session\n"); return -1; } if (set_outbound_authentication_credentials(&auth_sess, auths, challenge)) { ast_log(LOG_WARNING, "Failed to set authentication credentials\n"); return -1; } switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request)) { case PJ_SUCCESS: /* PJSIP creates a new transaction for new_request (meaning it creates a new * branch). However, it recycles the Call-ID, from-tag, and CSeq from the * original request. Some SIP implementations will not process the new request * since the CSeq is the same as the original request. Incrementing it here * fixes the interop issue */ cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL); ast_assert(cseq != NULL); ++cseq->cseq; return 0; case PJSIP_ENOCREDENTIAL: ast_log(LOG_WARNING, "Unable to create request with auth." "No auth credentials for any realms in challenge.\n"); break; case PJSIP_EAUTHSTALECOUNT: ast_log(LOG_WARNING, "Unable to create request with auth." "Number of stale retries exceeded\n"); break; case PJSIP_EFAILEDCREDENTIAL: ast_log(LOG_WARNING, "Authentication credentials not accepted by server\n"); break; default: ast_log(LOG_WARNING, "Unable to create request with auth. Unknown failure\n"); break; } return -1; }
static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth) { struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr; int challenge_found = 0; char nonce[64]; while ((auth_hdr = (pjsip_authorization_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, auth_hdr->next))) { ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce)); if (check_nonce(nonce, rdata, auth) && !pj_strcmp2(&auth_hdr->credential.digest.realm, auth->realm)) { challenge_found = 1; break; } } return challenge_found; }
/// Checks whether the supplied message contains the extension in the /// Supported header. pj_bool_t PJUtils::msg_supports_extension(pjsip_msg* msg, const char* extension) { pjsip_supported_hdr* supported_hdr = (pjsip_supported_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); if (!supported_hdr) { return PJ_FALSE; } for (unsigned ii = 0; ii < supported_hdr->count; ++ii) { if (pj_strcmp2(&supported_hdr->values[ii], extension) == 0) { return PJ_TRUE; } } return PJ_FALSE; }
static pjsip_authorization_hdr *get_auth_header(pjsip_rx_data *rdata, char *username, size_t username_size, char *realm, size_t realm_size, pjsip_authorization_hdr *start) { pjsip_authorization_hdr *header; header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, start); if (!header || pj_stricmp2(&header->scheme, "digest")) { return NULL; } ast_copy_pj_str(username, &header->credential.digest.username, username_size); ast_copy_pj_str(realm, &header->credential.digest.realm, realm_size); return header; }
static pjsip_www_authenticate_hdr *get_auth_header(pjsip_rx_data *challenge) { pjsip_hdr_e search_type; if (challenge->msg_info.msg->line.status.code == PJSIP_SC_UNAUTHORIZED) { search_type = PJSIP_H_WWW_AUTHENTICATE; } else if (challenge->msg_info.msg->line.status.code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) { search_type = PJSIP_H_PROXY_AUTHENTICATE; } else { ast_log(LOG_ERROR, "Status code %d was received when it should have been 401 or 407.\n", challenge->msg_info.msg->line.status.code); return NULL ; } return pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, NULL); }
bool SessionExpiresHelper::timer_supported(pjsip_msg* msg) { pjsip_supported_hdr* supported_hdr = (pjsip_supported_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); if (supported_hdr != NULL) { for (unsigned ii = 0; ii < supported_hdr->count; ++ii) { if (pj_strcmp(&supported_hdr->values[ii], &STR_TIMER) == 0) { return true; } } } return false; }
/*! * \internal * \brief Set an ast_party_id structure based on data in a From * * This makes use of \ref set_id_from_hdr for setting name and number. It uses * no information from the message in order to set privacy. It relies on endpoint * configuration for privacy information. * * \param rdata The incoming message * \param[out] id The ID to set * \retval 0 Succesfully set the party ID * \retval non-zero Could not set the party ID */ static int set_id_from_from(struct pjsip_rx_data *rdata, struct ast_party_id *id) { pjsip_fromto_hdr *from = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_FROM, rdata->msg_info.msg->hdr.next); if (!from) { /* This had better not happen */ return -1; } set_id_from_hdr(from, id); if (!id->number.valid) { return -1; } return 0; }
/** * Private: check if we can accept the message. */ pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata, pjsip_accept_hdr **p_accept_hdr) { /* Some UA sends text/html, so this check will break */ #if 0 pjsip_ctype_hdr *ctype; pjsip_msg *msg; msg = rdata->msg_info.msg; /* Request MUST have message body, with Content-Type equal to * "text/plain". */ ctype = (pjsip_ctype_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTENT_TYPE, NULL); if (msg->body == NULL || ctype == NULL || !acceptable_message(&ctype->media)) { /* Create Accept header. */ if (p_accept_hdr) *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool); return PJ_FALSE; } #elif 0 pjsip_msg *msg; msg = rdata->msg_info.msg; if (msg->body == NULL) { /* Create Accept header. */ if (p_accept_hdr) *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool); return PJ_FALSE; } #else /* Ticket #693: allow incoming MESSAGE without message body */ PJ_UNUSED_ARG(rdata); PJ_UNUSED_ARG(p_accept_hdr); #endif return PJ_TRUE; }
/// Adds a header indicating the message is integrity protected because it /// was received on a transport that has already been authenticated. void PJUtils::add_integrity_protected_indication(pjsip_tx_data* tdata) { LOG_INFO("Adding integrity-protected indicator to message"); pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); if (auth_hdr == NULL) { auth_hdr = pjsip_authorization_hdr_create(tdata->pool); auth_hdr->scheme = pj_str("Digest"); auth_hdr->credential.digest.realm = pj_str(""); auth_hdr->credential.digest.username = PJUtils::uri_to_pj_str(PJSIP_URI_IN_FROMTO_HDR, tdata->msg->line.req.uri, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)auth_hdr); } pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); new_param->name = STR_INTEGRITY_PROTECTED; new_param->value = pj_str("\"yes\""); pj_list_insert_before(&auth_hdr->credential.common.other_param, new_param); }
/// Apply the mangalgorithm to the From tag, To tag (if present) and call ID of /// req. void MangelwurzelTsx::mangle_dialog_identifiers(pjsip_msg* req, pj_pool_t* pool) { pjsip_from_hdr* from_hdr = PJSIP_MSG_FROM_HDR(req); if (from_hdr != NULL) { std::string from_tag = PJUtils::pj_str_to_string(&from_hdr->tag); mangle_string(from_tag); TRC_DEBUG("From tag mangled to %s", from_tag.c_str()); from_hdr->tag = pj_strdup3(pool, from_tag.c_str()); } pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); if (to_hdr != NULL) { std::string to_tag = PJUtils::pj_str_to_string(&to_hdr->tag); mangle_string(to_tag); TRC_DEBUG("To tag mangled to %s", to_tag.c_str()); to_hdr->tag = pj_strdup3(pool, to_tag.c_str()); } pjsip_cid_hdr* cid_hdr = (pjsip_cid_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CALL_ID, NULL); if (cid_hdr != NULL) { std::string call_id = PJUtils::pj_str_to_string(&cid_hdr->id); mangle_string(call_id); TRC_DEBUG("Call ID manged to %s", call_id.c_str()); cid_hdr->id = pj_strdup3(pool, call_id.c_str()); // Report a SAS marker for the new call ID so that the two dialogs can be // correlated in SAS. TRC_DEBUG("Logging SAS Call-ID marker, Call-ID %.*s", cid_hdr->id.slen, cid_hdr->id.ptr); SAS::Marker cid_marker(trail(), MARKER_ID_SIP_CALL_ID, 1u); cid_marker.add_var_param(cid_hdr->id.slen, cid_hdr->id.ptr); SAS::report_marker(cid_marker, SAS::Marker::Scope::Trace); } }
static int add_supported(pjsip_tx_data *tdata) { pjsip_supported_hdr *hdr; hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL); if (!hdr) { /* insert a new Supported header */ hdr = pjsip_supported_hdr_create(tdata->pool); if (!hdr) { return -1; } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr); } /* add on to the existing Supported header */ pj_strassign(&hdr->values[hdr->count++], &PATH_SUPPORTED_NAME); return 0; }
static int rewrite_contact(pjsip_rx_data *rdata, pjsip_dialog *dlg) { pjsip_contact_hdr *contact; contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); rewrite_uri(rdata, uri); if (dlg && pj_list_empty(&dlg->route_set) && (!dlg->remote.contact || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } return 0; } return -1; }
/// Substitutes the branch identifier in the top Via header with a new unique /// identifier. This is used when forking requests and when retrying requests /// to alternate servers. This code is taken from pjsip_generate_branch_id /// for the case when the branch ID is calculated from a GUID. void PJUtils::generate_new_branch_id(pjsip_tx_data* tdata) { pjsip_via_hdr* via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_BRANCH_LEN); via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN; pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); pj_str_t tmp; tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; // Add "Pj" between the RFC3261 prefix and the random string to be consistent // with branch IDs generated by PJSIP. *(tmp.ptr-2) = 'P'; *(tmp.ptr-1) = 'j'; pj_generate_unique_string(&tmp); via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; }
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; }
/// Checks whether the next Route header in the message refers to this node, /// and optionally returns the header. If there are no Route headers it /// returns false. pj_bool_t PJUtils::is_next_route_local(const pjsip_msg* msg, pjsip_route_hdr* start, pjsip_route_hdr** hdr) { bool rc = false; pjsip_route_hdr* route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ROUTE, (start != NULL) ? start->next : NULL); if (route_hdr != NULL) { // Found the next Route header, so check whether the URI corresponds to // this node or one of its aliases. pjsip_uri* uri = route_hdr->name_addr.uri; LOG_DEBUG("Found Route header, URI = %s", uri_to_string(PJSIP_URI_IN_ROUTING_HDR, uri).c_str()); if ((is_home_domain(uri)) || (is_uri_local(uri))) { rc = true; if (hdr != NULL) { *hdr = route_hdr; } } } return rc; }
/*! * \internal * \brief Session supplement callback for outgoing INVITE requests * * For an initial INVITE request, we may change the From header to appropriately * reflect the identity information. On all INVITEs (initial and reinvite) we may * add other identity headers such as P-Asserted-Identity and Remote-Party-ID based * on configuration and privacy settings * * \param session The session on which the INVITE will be sent * \param tdata The outbound INVITE request */ static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata) { struct ast_party_id effective_id; struct ast_party_id connected_id; if (!session->channel) { return; } /* Must do a deep copy unless we hold the channel lock the entire time. */ ast_party_id_init(&connected_id); ast_channel_lock(session->channel); effective_id = ast_channel_connected_effective_id(session->channel); ast_party_id_copy(&connected_id, &effective_id); ast_channel_unlock(session->channel); if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) { /* Only change the From header on the initial outbound INVITE. Switching it * mid-call might confuse some UAs. */ pjsip_fromto_hdr *from; pjsip_dialog *dlg; from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, tdata->msg->hdr.next); dlg = session->inv_session->dlg; if (ast_strlen_zero(session->endpoint->fromuser) && (session->endpoint->id.trust_outbound || (ast_party_id_presentation(&connected_id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED)) { modify_id_header(tdata->pool, from, &connected_id); modify_id_header(dlg->pool, dlg->local.info, &connected_id); } ast_sip_add_usereqphone(session->endpoint, tdata->pool, from->uri); ast_sip_add_usereqphone(session->endpoint, dlg->pool, dlg->local.info->uri); } add_id_headers(session, tdata, &connected_id); ast_party_id_free(&connected_id); }
void PJUtils::get_impi_and_impu(pjsip_rx_data* rdata, std::string& impi_out, std::string& impu_out) { // Check to see if the request has already been integrity protected? pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->uri); impu_out = PJUtils::public_id_from_uri(to_uri); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // private user identity is supplied in the Authorization header so use it. impi_out = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); LOG_DEBUG("Private identity from authorization header = %s", impi_out.c_str()); } else { // private user identity not supplied, so construct a default from the // public user identity by stripping the sip: prefix. impi_out = PJUtils::default_private_id_from_uri(to_uri); LOG_DEBUG("Private identity defaulted from public identity = %s", impi_out.c_str()); } }
static pj_bool_t handle_rx_message(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) { pjsip_contact_hdr *contact; if (!endpoint) { return PJ_FALSE; } if (endpoint->nat.rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && !contact->star && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); pj_cstr(&uri->host, rdata->pkt_info.src_name); if (strcasecmp("udp", rdata->tp_info.transport->type_name)) { uri->transport_param = pj_str(rdata->tp_info.transport->type_name); } else { uri->transport_param.slen = 0; } uri->port = rdata->pkt_info.src_port; ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n", (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port); /* rewrite the session target since it may have already been pulled from the contact header */ if (dlg && (!dlg->remote.contact || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*)pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } } if (endpoint->nat.force_rport) { rdata->msg_info.via->rport_param = rdata->pkt_info.src_port; } return PJ_FALSE; }
// 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_subscription_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri); pjsip_msg *msg = rdata->msg_info.msg; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_expires; } if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). LOG_ERROR("Rejecting subscribe request using invalid URI scheme"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } bool emergency_subscription = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { emergency_subscription = PJUtils::is_emergency_registration(contact_hdr); if (!emergency_subscription) { break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_subscription) { // Reject a subscription with a Contact header containing a contact address // that's been registered for emergency service. LOG_ERROR("Rejecting subscribe request from emergency registration"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &STR_ALLOW_EVENTS, &STR_REG); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_EVENT, NULL, (pjsip_hdr*)allow_events_hdr, NULL); return; } ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process SUBSCRIBE for public ID %s", public_id.c_str()); // Get the call identifier from the headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; // Subscriber must have already registered to be making a subscribe std::string state; HTTPCode http_code = hss->get_registration_data(public_id, state, ifc_map, uris, trail); if ((http_code != HTTP_OK) || (state != "REGISTERED")) { // We failed to get the list of associated URIs. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting SUBSCRIBE request"); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("aor = %s", aor.c_str()); LOG_DEBUG("SUBSCRIBE for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); // Write to the local store, checking the remote store if there is no entry locally. If the write to the local store succeeds, then write to the remote store. pjsip_tx_data* tdata_notify = NULL; RegStore::AoR* aor_data = NULL; std::string subscription_id; pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata, now, NULL, remote_store, &tdata_notify, &aor_data, true, subscription_id, trail); if (aor_data != NULL) { // Log the subscriptions. log_subscriptions(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { RegStore::AoR* remote_aor_data = NULL; std::string ignore; write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL, &tdata_notify, &remote_aor_data, false, ignore, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the subscribe with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response LOG_ERROR("Error building SUBSCRIBE %d response %s", st_code, PJUtils::pj_status_to_string(status).c_str()); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Error building SUBSCRIBE (" + std::to_string(st_code) + ") " + PJUtils::pj_status_to_string(status); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(tdata->pool, expiry); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); // Add the to tag to the response pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); pj_strdup2(tdata->pool, &to->tag, subscription_id.c_str()); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response. status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // Send the Notify if (tdata_notify != NULL && notify_status == PJ_SUCCESS) { set_trail(tdata_notify, trail); status = PJUtils::send_request(tdata_notify, 0, NULL, NULL, true); if (status != PJ_SUCCESS) { // LCOV_EXCL_START SAS::Event event(trail, SASEvent::NOTIFICATION_FAILED, 0); std::string error_msg = "Failed to send NOTIFY - error code: " + std::to_string(status); event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
/// Write to the registration store. pj_status_t write_subscriptions_to_store(RegStore* primary_store, ///<store to write to std::string aor, ///<address of record to write to pjsip_rx_data* rdata, ///<received message to read headers from int now, ///<time now RegStore::AoR* backup_aor, ///<backup data if no entry in store RegStore* backup_store, ///<backup store to read from if no entry in store and no backup data pjsip_tx_data** tdata_notify, ///<tdata to construct a SIP NOTIFY from RegStore::AoR** aor_data, ///<aor_data to write to bool update_notify, ///<whether to generate a SIP NOTIFY std::string& subscription_id, SAS::TrailId trail) { // Parse the 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; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); pjsip_fromto_hdr* from = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL); pjsip_fromto_hdr* to = (pjsip_fromto_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL); // The registration store 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. bool backup_aor_alloced = false; int expiry = 0; pj_status_t status = PJ_FALSE; (*aor_data) = NULL; do { // delete NULL is safe, so we can do this on every iteration. delete (*aor_data); // Find the current subscriptions for the AoR. (*aor_data) = primary_store->get_aor_data(aor, trail); 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. // LCOV_EXCL_START - local store (used in testing) never fails LOG_ERROR("Failed to get AoR subscriptions for %s from store", aor.c_str()); break; // LCOV_EXCL_STOP } // If we don't have any subscriptions, try the backup AoR and/or store. if ((*aor_data)->subscriptions().empty()) { if ((backup_aor == NULL) && (backup_store != NULL)) { backup_aor = backup_store->get_aor_data(aor, trail); backup_aor_alloced = (backup_aor != NULL); } if ((backup_aor != NULL) && (!backup_aor->subscriptions().empty())) { for (RegStore::AoR::Subscriptions::const_iterator i = backup_aor->subscriptions().begin(); i != backup_aor->subscriptions().end(); ++i) { RegStore::AoR::Subscription* src = i->second; RegStore::AoR::Subscription* dst = (*aor_data)->get_subscription(i->first); *dst = *src; } } } pjsip_contact_hdr* contact = (pjsip_contact_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (contact != NULL) { std::string contact_uri; pjsip_uri* uri = (contact->uri != NULL) ? (pjsip_uri*)pjsip_uri_get_uri(contact->uri) : NULL; if ((uri != NULL) && (PJSIP_URI_SCHEME_IS_SIP(uri))) { contact_uri = PJUtils::uri_to_string(PJSIP_URI_IN_CONTACT_HDR, uri); } subscription_id = PJUtils::pj_str_to_string(&to->tag); if (subscription_id == "") { // If there's no to tag, generate an unique one subscription_id = std::to_string(Utils::generate_unique_integer(id_deployment, id_instance)); } LOG_DEBUG("Subscription identifier = %s", subscription_id.c_str()); // Find the appropriate subscription in the subscription list for this AoR. If it can't // be found a new empty subscription is created. RegStore::AoR::Subscription* subscription = (*aor_data)->get_subscription(subscription_id); // Update/create the subscription. subscription->_req_uri = contact_uri; subscription->_route_uris.clear(); pjsip_route_hdr* route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, NULL); while (route_hdr) { std::string route = PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, route_hdr->name_addr.uri); LOG_DEBUG("Route header %s", route.c_str()); // Add the route. subscription->_route_uris.push_back(route); // Look for the next header. route_hdr = (pjsip_route_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, route_hdr->next); } subscription->_cid = cid; subscription->_to_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, to->uri); subscription->_to_tag = subscription_id; subscription->_from_uri = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, from->uri); subscription->_from_tag = PJUtils::pj_str_to_string(&from->tag); // Calculate the expiry period for the subscription. expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_expires; } subscription->_expires = now + expiry; std::map<std::string, RegStore::AoR::Binding> bindings; for (RegStore::AoR::Bindings::const_iterator i = (*aor_data)->bindings().begin(); i != (*aor_data)->bindings().end(); ++i) { std::string id = i->first; RegStore::AoR::Binding bind = *(i->second); if (!bind._emergency_registration) { bindings.insert(std::pair<std::string, RegStore::AoR::Binding>(id, bind)); } } if (update_notify) { NotifyUtils::SubscriptionState state = NotifyUtils::SubscriptionState::ACTIVE; if (expiry == 0) { state = NotifyUtils::SubscriptionState::TERMINATED; } status = NotifyUtils::create_notify(tdata_notify, subscription, aor, (*aor_data)->_notify_cseq, bindings, NotifyUtils::DocState::FULL, NotifyUtils::RegistrationState::ACTIVE, NotifyUtils::ContactState::ACTIVE, NotifyUtils::ContactEvent::REGISTERED, state, expiry); } if (analytics != NULL) { // Generate an analytics log for this subscription update. analytics->subscription(aor, subscription_id, contact_uri, expiry); } } } while (!primary_store->set_aor_data(aor, (*aor_data), false, trail)); // If we allocated the backup AoR, tidy up. if (backup_aor_alloced) { delete backup_aor; } return status; }
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); } } }
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc, const pjsip_rx_data *rdata, unsigned *contact_cnt, unsigned max_contact, pjsip_contact_hdr *contacts[]) { pj_int32_t expiration = NOEXP; const pjsip_msg *msg = rdata->msg_info.msg; const pjsip_hdr *hdr; /* Enumerate all Contact headers in the response */ *contact_cnt = 0; for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { if (hdr->type == PJSIP_H_CONTACT && *contact_cnt < max_contact) { contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr; ++(*contact_cnt); } } if (regc->current_op == REGC_REGISTERING) { pj_bool_t has_our_contact = PJ_FALSE; const pjsip_expires_hdr *expires; /* Get Expires header */ expires = (const pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); /* Try to find the Contact URIs that we register, in the response * to get the expires value. We'll try both with comparing the URI * and comparing the extension param only. */ if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) { unsigned i; for (i=0; i<*contact_cnt; ++i) { const pjsip_contact_hdr *our_hdr; our_hdr = (const pjsip_contact_hdr*) regc->contact_hdr_list.next; /* Match with our Contact header(s) */ while ((void*)our_hdr != (void*)®c->contact_hdr_list) { const pjsip_uri *uri1, *uri2; pj_bool_t matched = PJ_FALSE; /* Exclude the display name when comparing the URI * since server may not return it. */ uri1 = (const pjsip_uri*) pjsip_uri_get_uri(contacts[i]->uri); uri2 = (const pjsip_uri*) pjsip_uri_get_uri(our_hdr->uri); /* First try with exact matching, according to RFC 3261 * Section 19.1.4 URI Comparison */ if (pjsip_cfg()->regc.check_contact) { matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, uri1, uri2)==0; } /* If no match is found, try with matching the extension * parameter only if extension parameter was added. */ if (!matched && regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(uri1) || PJSIP_URI_SCHEME_IS_SIPS(uri1)) && (PJSIP_URI_SCHEME_IS_SIP(uri2) || PJSIP_URI_SCHEME_IS_SIPS(uri2))) { const pjsip_sip_uri *sip_uri1, *sip_uri2; const pjsip_param *p1, *p2; sip_uri1 = (const pjsip_sip_uri*)uri1; sip_uri2 = (const pjsip_sip_uri*)uri2; p1 = pjsip_param_cfind(&sip_uri1->other_param, &XUID_PARAM_NAME); p2 = pjsip_param_cfind(&sip_uri2->other_param, &XUID_PARAM_NAME); matched = p1 && p2 && pj_strcmp(&p1->value, &p2->value)==0; } if (matched) { has_our_contact = PJ_TRUE; if (contacts[i]->expires >= 0 && contacts[i]->expires < expiration) { /* Get the lowest expiration time. */ expiration = contacts[i]->expires; } break; } our_hdr = our_hdr->next; } /* while ((void.. */ } /* for (i=.. */ /* If matching Contact header(s) are found but the * header doesn't contain expires parameter, get the * expiration value from the Expires header. And * if Expires header is not present, get the expiration * value from the request. */ if (has_our_contact && expiration == NOEXP) { if (expires) { expiration = expires->ivalue; } else if (regc->expires_hdr) { expiration = regc->expires_hdr->ivalue; } else { /* We didn't request explicit expiration value, * and server doesn't specify it either. This * shouldn't happen unless we have a broken * registrar. */ expiration = 3600; } } } /* If we still couldn't get matching Contact header(s), it means * there must be something wrong with the registrar (e.g. it may * have modified the URI's in the response, which is prohibited). */ if (expiration==NOEXP) { /* If the number of Contact headers in the response matches * ours, they're all probably ours. Get the expiration * from there if this is the case, or from Expires header * if we don't have exact Contact header count, or * from the request as the last resort. */ pj_size_t our_contact_cnt; our_contact_cnt = pj_list_size(®c->contact_hdr_list); if (*contact_cnt == our_contact_cnt && *contact_cnt && contacts[0]->expires >= 0) { expiration = contacts[0]->expires; } else if (expires) expiration = expires->ivalue; else if (regc->expires_hdr) expiration = regc->expires_hdr->ivalue; else expiration = 3600; } } else { /* Just assume that the unregistration has been successful. */ expiration = 0; } /* Must have expiration value by now */ pj_assert(expiration != NOEXP); return expiration; }
static pj_status_t create_request(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); /* Create the request. */ status = pjsip_endpt_create_request_from_hdr( regc->endpt, pjsip_get_register_method(), regc->srv_url, regc->from_hdr, regc->to_hdr, NULL, regc->cid_hdr, regc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ pjsip_auth_clt_init_req( ®c->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ if (!pj_list_empty(®c->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; route_pos = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (!route_pos) route_pos = &tdata->msg->hdr; route = regc->route_set.next; while (route != ®c->route_set) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; route = route->next; } } /* Add additional request headers */ if (!pj_list_empty(®c->hdr_list)) { const pjsip_hdr *hdr; hdr = regc->hdr_list.next; while (hdr != ®c->hdr_list) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, new_hdr); hdr = hdr->next; } } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; }
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, ®c->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, ®c_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; }