From 0b62e416541066c53088013fc99a0775f9b318e3 Mon Sep 17 00:00:00 2001 From: Mark Michelson <mmichelson@digium.com> Date: Fri, 27 Mar 2015 20:46:55 +0000 Subject: [PATCH] Add stateful PJSIP response API call, and use it for out-of-dialog responses. Asterisk had an issue where retransmissions of MESSAGE requests resulted in Asterisk processing the retransmission as if it were a new MESSAGE request. This patch fixes the issue by creating a transaction in PJSIP on the incoming request. This way, if a retransmission arrives, the PJSIP transaction layer will resend the response and Asterisk will not ever see the retransmission. ASTERISK-24920 #close Reported by Mark Michelson Review: https://reviewboard.asterisk.org/r/4532/ ........ Merged revisions 433619 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@433620 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/res_pjsip.h | 23 +++++++++++++++++++++++ res/res_pjsip.c | 30 +++++++++++++++++++++++++++--- res/res_pjsip/pjsip_options.c | 12 +----------- res/res_pjsip_messaging.c | 10 +--------- res/res_pjsip_registrar.c | 7 +------ 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 442ee72f77..6e3a9ea930 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -1281,6 +1281,11 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code, /*! * \brief Send a response to an out of dialog request * + * Use this function sparingly, since this does not create a transaction + * within PJSIP. This means that if the request is retransmitted, it is + * your responsibility to detect this and not process the same request + * twice, and to send the same response for each retransmission. + * * \param res_addr The response address for this response * \param tdata The response to send * \param endpoint The ast_sip_endpoint associated with this response @@ -1290,6 +1295,24 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code, */ int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint); +/*! + * \brief Send a stateful response to an out of dialog request + * + * This creates a transaction within PJSIP, meaning that if the request + * that we are responding to is retransmitted, we will not attempt to + * re-handle the request. + * + * \param rdata The request that is being responded to + * \param tdata The response to send + * \param endpoint The ast_sip_endpoint associated with this response + * + * \since 13.4.0 + * + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint); + /*! * \brief Determine if an incoming request requires authentication * diff --git a/res/res_pjsip.c b/res/res_pjsip.c index e5af28ca82..64cd43e184 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -3298,7 +3298,7 @@ static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata) return PJ_FALSE; } -int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint) +static void supplement_outgoing_response(pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint) { struct ast_sip_supplement *supplement; pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); @@ -3306,8 +3306,7 @@ int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, s AST_RWLIST_RDLOCK(&supplements); AST_LIST_TRAVERSE(&supplements, supplement, next) { - if (supplement->outgoing_response - && does_method_match(&cseq->method.name, supplement->method)) { + if (supplement->outgoing_response && does_method_match(&cseq->method.name, supplement->method)) { supplement->outgoing_response(sip_endpoint, contact, tdata); } } @@ -3315,10 +3314,35 @@ int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, s ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL); ao2_cleanup(contact); +} + +int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint) +{ + supplement_outgoing_response(tdata, sip_endpoint); return pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), res_addr, tdata, NULL, NULL); } +int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint) +{ + pjsip_transaction *tsx; + + if (pjsip_tsx_create_uas(NULL, rdata, &tsx) != PJ_SUCCESS) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + pjsip_tsx_recv_msg(tsx, rdata); + + supplement_outgoing_response(tdata, sip_endpoint); + + if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + return 0; +} + int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code, struct ast_sip_contact *contact, pjsip_tx_data **tdata) { diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index 0b14bed921..9794827b56 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -577,7 +577,6 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code) pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata); pjsip_tx_data *tdata; const pjsip_hdr *hdr; - pjsip_response_addr res_addr; pj_status_t status; /* Make the response object */ @@ -611,17 +610,8 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code) } else { struct ast_sip_endpoint *endpoint; - /* Get where to send response. */ - status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); - if (status != PJ_SUCCESS) { - ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status); - - pjsip_tx_data_dec_ref(tdata); - return status; - } - endpoint = ast_pjsip_rdata_get_endpoint(rdata); - status = ast_sip_send_response(&res_addr, tdata, endpoint); + status = ast_sip_send_stateful_response(rdata, tdata, endpoint); ao2_cleanup(endpoint); } diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c index f3ae5e674e..5caf893740 100644 --- a/res/res_pjsip_messaging.c +++ b/res/res_pjsip_messaging.c @@ -610,7 +610,6 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co { pjsip_tx_data *tdata; pj_status_t status; - pjsip_response_addr res_addr; status = ast_sip_create_response(rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) { @@ -623,15 +622,8 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co } else { struct ast_sip_endpoint *endpoint; - /* Get where to send response. */ - status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); - if (status != PJ_SUCCESS) { - ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status); - return status; - } - endpoint = ast_pjsip_rdata_get_endpoint(rdata); - status = ast_sip_send_response(&res_addr, tdata, endpoint); + status = ast_sip_send_stateful_response(rdata, tdata, endpoint); ao2_cleanup(endpoint); } diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index 7fe2b77d9f..944a6055ef 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -418,7 +418,6 @@ static int rx_task(void *data) pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; - pjsip_response_addr addr; const char *aor_name = ast_sorcery_object_get_id(task_data->aor); RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; @@ -603,11 +602,7 @@ static int rx_task(void *data) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } - if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) { - ast_sip_send_response(&addr, tdata, task_data->endpoint); - } else { - pjsip_tx_data_dec_ref(tdata); - } + ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint); return PJ_TRUE; } -- GitLab