/*! * \internal * \brief Create an identity header for an outgoing message * \param hdr_name The name of the header to create * \param tdata The message to place the header on * \param id The identification information for the new header * \return newly-created header */ static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_tx_data *tdata, const struct ast_party_id *id) { pjsip_fromto_hdr *id_hdr; pjsip_fromto_hdr *base; pjsip_name_addr *id_name_addr; pjsip_sip_uri *id_uri; base = tdata->msg->type == PJSIP_REQUEST_MSG ? PJSIP_MSG_FROM_HDR(tdata->msg) : PJSIP_MSG_TO_HDR(tdata->msg); id_hdr = pjsip_from_hdr_create(tdata->pool); id_hdr->type = PJSIP_H_OTHER; pj_strdup(tdata->pool, &id_hdr->name, hdr_name); id_hdr->sname.slen = 0; id_name_addr = pjsip_uri_clone(tdata->pool, base->uri); id_uri = pjsip_uri_get_uri(id_name_addr->uri); if (id->name.valid) { int name_buf_len = strlen(id->name.str) * 2 + 1; char *name_buf = ast_alloca(name_buf_len); ast_escape_quoted(id->name.str, name_buf, name_buf_len); pj_strdup2(tdata->pool, &id_name_addr->display, name_buf); } pj_strdup2(tdata->pool, &id_uri->user, id->number.str); id_hdr->uri = (pjsip_uri *) id_name_addr; return id_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; 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); }
/*! * \internal * \brief Create an identity header for an outgoing message * \param hdr_name The name of the header to create * \param tdata The message to place the header on * \param id The identification information for the new header * \return newly-created header */ static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_fromto_hdr *base, pjsip_tx_data *tdata, const struct ast_party_id *id) { pjsip_fromto_hdr *id_hdr; pjsip_name_addr *id_name_addr; pjsip_sip_uri *id_uri; id_hdr = pjsip_from_hdr_create(tdata->pool); id_hdr->type = PJSIP_H_OTHER; pj_strdup(tdata->pool, &id_hdr->name, hdr_name); id_hdr->sname.slen = 0; id_name_addr = pjsip_uri_clone(tdata->pool, base->uri); id_uri = pjsip_uri_get_uri(id_name_addr->uri); if (id->name.valid) { int name_buf_len = strlen(id->name.str) * 2 + 1; char *name_buf = ast_alloca(name_buf_len); ast_escape_quoted(id->name.str, name_buf, name_buf_len); pj_strdup2(tdata->pool, &id_name_addr->display, name_buf); } else { /* * We need to clear the remnants of the clone or it'll be left set. * pj_strdup2 is safe to call with a NULL src and it resets both slen and ptr. */ pj_strdup2(tdata->pool, &id_name_addr->display, NULL); } pj_strdup2(tdata->pool, &id_uri->user, id->number.str); id_hdr->uri = (pjsip_uri *) id_name_addr; return id_hdr; }
/// Mangelwurzel receives an initial request. It will Record-Route itself, /// strip off all the Via headers and send the request on. It can also change /// the request in various ways depending on the configuration in its Route /// header. /// - It can mangle the dialog identifiers using its mangalgorithm. /// - It can mangle the Request URI and Contact URI using its mangalgorithm. /// - It can mangle the To URI using its mangalgorithm. /// - It can edit the S-CSCF Route header to turn the request into either an /// originating or terminating request. /// - It can edit the S-CSCF Route header to turn the request into an out of /// the blue request. /// - It can mangle the Record-Route headers URIs. void MangelwurzelTsx::on_rx_initial_request(pjsip_msg* req) { // Store off the unmodified request. _unmodified_request = original_request(); // If Mangelwurzel receives a REGISTER, we need to respond with a 200 OK // rather than mangling the request and forwarding it on. if (req->line.req.method.id == PJSIP_REGISTER_METHOD) { pjsip_msg* rsp = create_response(req, PJSIP_SC_OK); send_response(rsp); free_msg(req); return; } pj_pool_t* pool = get_pool(req); // Get Mangelwurzel's route header and clone the URI. We use this in the SAS // event logging that we've received a request, and then we use it to // Record-Route ourselves. const pjsip_route_hdr* mangelwurzel_route_hdr = route_hdr(); pjsip_uri* mangelwurzel_uri = (pjsip_uri*)pjsip_uri_clone(pool, mangelwurzel_route_hdr->name_addr.uri); SAS::Event event(trail(), SASEvent::MANGELWURZEL_INITIAL_REQ, 0); event.add_var_param(PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, mangelwurzel_uri)); SAS::report_event(event); if (_config.dialog) { mangle_dialog_identifiers(req, pool); } if (_config.req_uri) { mangle_req_uri(req, pool); mangle_contact(req, pool); } if (_config.to) { mangle_to(req, pool); } edit_scscf_route_hdr(req, pool); if (_config.routes) { mangle_record_routes(req, pool); } strip_via_hdrs(req); record_route(req, pool, mangelwurzel_uri); send_request(req); }
/// Route the request to the BGCF. void ICSCFSproutletTsx::route_to_bgcf(pjsip_msg* req) { TRC_INFO("Routing to BGCF %s", PJUtils::uri_to_string(PJSIP_URI_IN_ROUTING_HDR, _icscf->bgcf_uri()).c_str()); PJUtils::add_route_header(req, (pjsip_sip_uri*)pjsip_uri_clone(get_pool(req), _icscf->bgcf_uri()), get_pool(req)); send_request(req); _routed_to_bgcf = true; }
/*! * \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); }
PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, const pjsip_name_addr *src) { pj_strdup( pool, &dst->display, &src->display); dst->uri = (pjsip_uri*) pjsip_uri_clone(pool, src->uri); }
/* * Create PRACK request for the incoming reliable provisional response. */ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_tx_data **p_tdata) { dlg_data *dd; uac_state_t *uac_state = NULL; const pj_str_t *to_tag = &rdata->msg_info.to->tag; pjsip_transaction *tsx; pjsip_msg *msg; pjsip_generic_string_hdr *rseq_hdr; pjsip_generic_string_hdr *rack_hdr; unsigned rseq; pj_str_t rack; char rack_buf[80]; pjsip_tx_data *tdata; pj_status_t status; *p_tdata = NULL; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED); tsx = pjsip_rdata_get_tsx(rdata); msg = rdata->msg_info.msg; /* Check our assumptions */ pj_assert( tsx->role == PJSIP_ROLE_UAC && tsx->method.id == PJSIP_INVITE_METHOD && msg->line.status.code > 100 && msg->line.status.code < 200); /* Get the RSeq header */ rseq_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); if (rseq_hdr == NULL) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response with no RSeq header")); return PJSIP_EMISSINGHDR; } rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); /* Find UAC state for the specified call leg */ uac_state = dd->uac_state_list; while (uac_state) { if (pj_stricmp(&uac_state->tag, to_tag)==0) break; uac_state = uac_state->next; } /* Create new UAC state if we don't have one */ if (uac_state == NULL) { uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t); uac_state->cseq = rdata->msg_info.cseq->cseq; uac_state->rseq = rseq - 1; pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag); uac_state->next = dd->uac_state_list; dd->uac_state_list = uac_state; } /* If this is from new INVITE transaction, reset UAC state. */ if (rdata->msg_info.cseq->cseq != uac_state->cseq) { uac_state->cseq = rdata->msg_info.cseq->cseq; uac_state->rseq = rseq - 1; } /* Ignore provisional response retransmission */ if (rseq <= uac_state->rseq) { /* This should have been handled before */ return PJ_EIGNORED; /* Ignore provisional response with out-of-order RSeq */ } else if (rseq != uac_state->rseq + 1) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response because RSeq jump " "(expecting %u, got %u)", uac_state->rseq+1, rseq)); return PJ_EIGNORED; } /* Update our RSeq */ uac_state->rseq = rseq; /* Create PRACK */ status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, -1, &tdata); if (status != PJ_SUCCESS) return status; /* If this response is a forked response from a different call-leg, * update the req URI (https://trac.pjsip.org/repos/ticket/1364) */ if (pj_stricmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { const pjsip_contact_hdr *mhdr; mhdr = (const pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (!mhdr || !mhdr->uri) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response with no or " "invalid Contact header")); pjsip_tx_data_dec_ref(tdata); return PJ_EIGNORED; } tdata->msg->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, mhdr->uri); } /* Create RAck header */ rack.ptr = rack_buf; rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), "%u %u %.*s", rseq, rdata->msg_info.cseq->cseq, (int)tsx->method.name.slen, tsx->method.name.ptr); if (rack.slen < 1 || rack.slen >= (int)sizeof(rack_buf)) { return PJ_ETOOSMALL; } rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr); /* Done */ *p_tdata = tdata; return PJ_SUCCESS; }
/* * Create new request message to be forwarded upstream to new destination URI * in uri. */ PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt, pjsip_rx_data *rdata, const pjsip_uri *uri, const pj_str_t *branch, unsigned options, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_UNUSED_ARG(options); /* Request forwarding rule in RFC 3261 section 16.6: * * For each target, the proxy forwards the request following these * steps: * * 1. Make a copy of the received request * 2. Update the Request-URI * 3. Update the Max-Forwards header field * 4. Optionally add a Record-route header field value * 5. Optionally add additional header fields * 6. Postprocess routing information * 7. Determine the next-hop address, port, and transport * 8. Add a Via header field value * 9. Add a Content-Length header field if necessary * 10. Forward the new request * * Of these steps, we only do step 1-3, since the later will be * done by application. */ status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Always increment ref counter to 1 */ pjsip_tx_data_add_ref(tdata); /* Duplicate the request */ PJ_TRY { pjsip_msg *dst; const pjsip_msg *src = rdata->msg_info.msg; const pjsip_hdr *hsrc; /* Create the request */ tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); /* Duplicate request method */ pjsip_method_copy(tdata->pool, &tdata->msg->line.req.method, &src->line.req.method); /* Set request URI */ if (uri) { dst->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, uri); } else { dst->line.req.uri= (pjsip_uri*) pjsip_uri_clone(tdata->pool, src->line.req.uri); } /* Clone ALL headers */ hsrc = src->hdr.next; while (hsrc != &src->hdr) { pjsip_hdr *hdst; /* If this is the top-most Via header, insert our own before * cloning the header. */ if (hsrc == (pjsip_hdr*)rdata->msg_info.via) { pjsip_via_hdr *hvia; hvia = pjsip_via_hdr_create(tdata->pool); if (branch) pj_strdup(tdata->pool, &hvia->branch_param, branch); else { pj_str_t new_branch = pjsip_calculate_branch_id(rdata); pj_strdup(tdata->pool, &hvia->branch_param, &new_branch); } pjsip_msg_add_hdr(dst, (pjsip_hdr*)hvia); } /* Skip Content-Type and Content-Length as these would be * generated when the the message is printed. */ else if (hsrc->type == PJSIP_H_CONTENT_LENGTH || hsrc->type == PJSIP_H_CONTENT_TYPE) { hsrc = hsrc->next; continue; } #if 0 /* If this is the top-most Route header and it indicates loose * route, remove the header. */ else if (hsrc == (pjsip_hdr*)rdata->msg_info.route) { const pjsip_route_hdr *hroute = (const pjsip_route_hdr*) hsrc; const pjsip_sip_uri *sip_uri; if (!PJSIP_URI_SCHEME_IS_SIP(hroute->name_addr.uri) && !PJSIP_URI_SCHEME_IS_SIPS(hroute->name_addr.uri)) { /* This is a bad request! */ status = PJSIP_EINVALIDHDR; goto on_error; } sip_uri = (pjsip_sip_uri*) hroute->name_addr.uri; if (sip_uri->lr_param) { /* Yes lr param is present, skip this Route header */ hsrc = hsrc->next; continue; } } #endif /* Clone the header */ hdst = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hsrc); /* If this is Max-Forward header, decrement the value */ if (hdst->type == PJSIP_H_MAX_FORWARDS) { pjsip_max_fwd_hdr *hmaxfwd = (pjsip_max_fwd_hdr*)hdst; --hmaxfwd->ivalue; } /* Append header to new request */ pjsip_msg_add_hdr(dst, hdst); hsrc = hsrc->next; } /* 16.6.3: * If the copy does not contain a Max-Forwards header field, the * proxy MUST add one with a field value, which SHOULD be 70. */ if (rdata->msg_info.max_fwd == NULL) { pjsip_max_fwd_hdr *hmaxfwd = pjsip_max_fwd_hdr_create(tdata->pool, 70); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hmaxfwd); } /* Clone request body */ if (src->body) { dst->body = pjsip_msg_body_clone(tdata->pool, src->body); } } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END /* Done */ *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; }