From b2ef13cb609b31702cfd34063712848d41575421 Mon Sep 17 00:00:00 2001 From: Richard Mudgett <rmudgett@digium.com> Date: Tue, 15 Feb 2011 16:18:43 +0000 Subject: [PATCH] Merged revisions 307879 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.8 ........ r307879 | rmudgett | 2011-02-15 10:13:55 -0600 (Tue, 15 Feb 2011) | 37 lines No response sent for SIP CC subscribe/resubscribe request. Asterisk does not send a response if we try to subscribe for call completion after we have received a 180 Ringing. You can only subscribe for call completion when the call has been cleared. When we receive the 180 Ringing, for this call, its call-completion state is 'CC_AVAILABLE'. If we then send a subscribe message to Asterisk, it trys to change the call-completion state to 'CC_CALLER_REQUESTED'. Because this is an invalid state change, it just ignores the message. The only state Asterisk will accept our subscribe message is in the 'CC_CALLER_OFFERED' state. Asterisk will go into the 'CC_CALLER_OFFERED' when the SIP client clears the call by sending a CANCEL. Asterisk should always send a response. Even if its a negative one. The fix is to allow for the CCSS core to notify a CC agent that a failure has occurred when CC is requested. The "ack" callback is replaced with a "respond" callback. The "respond" callback has a parameter indicating either a successful response or a specific type of failure that may need to be communicated to the requester. (closes issue #18336) Reported by: GeorgeKonopacki Tested by: mmichelson, rmudgett JIRA SWP-2633 (closes issue #18337) Reported by: GeorgeKonopacki Tested by: mmichelson JIRA SWP-2634 ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@307883 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_dahdi.c | 2 +- channels/chan_sip.c | 26 +++++++++++++++---- channels/sig_pri.c | 57 +++++++++++++++++++++++++++++++++-------- channels/sig_pri.h | 2 +- include/asterisk/ccss.h | 23 +++++++++++++---- main/ccss.c | 39 ++++++++++++++++++++-------- 6 files changed, 117 insertions(+), 32 deletions(-) diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 888a8f27fa..58e275350e 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -16205,7 +16205,7 @@ static struct ast_cc_agent_callbacks dahdi_pri_cc_agent_callbacks = { .init = dahdi_pri_cc_agent_init, .start_offer_timer = sig_pri_cc_agent_start_offer_timer, .stop_offer_timer = sig_pri_cc_agent_stop_offer_timer, - .ack = sig_pri_cc_agent_req_ack, + .respond = sig_pri_cc_agent_req_rsp, .status_request = sig_pri_cc_agent_status_req, .stop_ringing = sig_pri_cc_agent_stop_ringing, .party_b_free = sig_pri_cc_agent_party_b_free, diff --git a/channels/chan_sip.c b/channels/chan_sip.c index ed4f0162b0..06e23ad07f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1614,7 +1614,7 @@ struct ast_channel_tech sip_tech_info; static int sip_cc_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan); static int sip_cc_agent_start_offer_timer(struct ast_cc_agent *agent); static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent); -static void sip_cc_agent_ack(struct ast_cc_agent *agent); +static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason); static int sip_cc_agent_status_request(struct ast_cc_agent *agent); static int sip_cc_agent_start_monitoring(struct ast_cc_agent *agent); static int sip_cc_agent_recall(struct ast_cc_agent *agent); @@ -1625,7 +1625,7 @@ static struct ast_cc_agent_callbacks sip_cc_agent_callbacks = { .init = sip_cc_agent_init, .start_offer_timer = sip_cc_agent_start_offer_timer, .stop_offer_timer = sip_cc_agent_stop_offer_timer, - .ack = sip_cc_agent_ack, + .respond = sip_cc_agent_respond, .status_request = sip_cc_agent_status_request, .start_monitoring = sip_cc_agent_start_monitoring, .callee_available = sip_cc_agent_recall, @@ -1726,14 +1726,30 @@ static int sip_cc_agent_stop_offer_timer(struct ast_cc_agent *agent) return 0; } -static void sip_cc_agent_ack(struct ast_cc_agent *agent) +static void sip_cc_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason) { struct sip_cc_agent_pvt *agent_pvt = agent->private_data; sip_pvt_lock(agent_pvt->subscribe_pvt); ast_set_flag(&agent_pvt->subscribe_pvt->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq); - transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED); + if (reason == AST_CC_AGENT_RESPONSE_SUCCESS || !ast_strlen_zero(agent_pvt->notify_uri)) { + /* The second half of this if statement may be a bit hard to grasp, + * so here's an explanation. When a subscription comes into + * chan_sip, as long as it is not malformed, it will be passed + * to the CC core. If the core senses an out-of-order state transition, + * then the core will call this callback with the "reason" set to a + * failure condition. + * However, an out-of-order state transition will occur during a resubscription + * for CC. In such a case, we can see that we have already generated a notify_uri + * and so we can detect that this isn't a *real* failure. Rather, it is just + * something the core doesn't recognize as a legitimate SIP state transition. + * Thus we respond with happiness and flowers. + */ + transmit_response(agent_pvt->subscribe_pvt, "200 OK", &agent_pvt->subscribe_pvt->initreq); + transmit_cc_notify(agent, agent_pvt->subscribe_pvt, CC_QUEUED); + } else { + transmit_response(agent_pvt->subscribe_pvt, "500 Internal Error", &agent_pvt->subscribe_pvt->initreq); + } sip_pvt_unlock(agent_pvt->subscribe_pvt); agent_pvt->is_available = TRUE; } diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 58f6c82b2b..b3117f533b 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -8542,38 +8542,75 @@ int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent) #if defined(HAVE_PRI_CCSS) /*! - * \brief Acknowledge CC request. + * \brief Response to a CC request. * \since 1.8 * * \param agent CC core agent control. + * \param reason CC request response status. * * \details * When the core receives knowledge that a called * party has accepted a CC request, it will call - * this callback. + * this callback. The core may also call this + * if there is some error when attempting to process + * the incoming CC request. * - * The duty of this is to accept a CC request from - * the caller by acknowledging receipt of that request. + * The duty of this is to issue a propper response to a + * CC request from the caller by acknowledging receipt + * of that request or rejecting it. * * \return Nothing */ -void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent) +void sig_pri_cc_agent_req_rsp(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason) { struct sig_pri_cc_agent_prv *cc_pvt; int res; + int status; + const char *failed_msg; + static const char *failed_to_send = "Failed to send the CC request response."; + static const char *not_accepted = "The core declined the CC request."; cc_pvt = agent->private_data; ast_mutex_lock(&cc_pvt->pri->lock); if (cc_pvt->cc_request_response_pending) { cc_pvt->cc_request_response_pending = 0; - res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, 0/* success */); + + /* Convert core response reason to ISDN response status. */ + status = 2;/* short_term_denial */ + switch (reason) { + case AST_CC_AGENT_RESPONSE_SUCCESS: + status = 0;/* success */ + break; + case AST_CC_AGENT_RESPONSE_FAILURE_INVALID: + status = 2;/* short_term_denial */ + break; + case AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY: + status = 5;/* queue_full */ + break; + } + + res = pri_cc_req_rsp(cc_pvt->pri->pri, cc_pvt->cc_id, status); + if (!status) { + /* CC core request was accepted. */ + if (res) { + failed_msg = failed_to_send; + } else { + failed_msg = NULL; + } + } else { + /* CC core request was declined. */ + if (res) { + failed_msg = failed_to_send; + } else { + failed_msg = not_accepted; + } + } } else { - res = 0; + failed_msg = NULL; } ast_mutex_unlock(&cc_pvt->pri->lock); - if (res) { - ast_cc_failed(agent->core_id, "%s agent failed to send the CC request ack.", - sig_pri_cc_type_name); + if (failed_msg) { + ast_cc_failed(agent->core_id, "%s agent: %s", sig_pri_cc_type_name, failed_msg); } } #endif /* defined(HAVE_PRI_CCSS) */ diff --git a/channels/sig_pri.h b/channels/sig_pri.h index ae2f97c52d..14383963fd 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -591,7 +591,7 @@ void sig_pri_sendtext(struct sig_pri_chan *pchan, const char *text); int sig_pri_cc_agent_init(struct ast_cc_agent *agent, struct sig_pri_chan *pvt_chan); int sig_pri_cc_agent_start_offer_timer(struct ast_cc_agent *agent); int sig_pri_cc_agent_stop_offer_timer(struct ast_cc_agent *agent); -void sig_pri_cc_agent_req_ack(struct ast_cc_agent *agent); +void sig_pri_cc_agent_req_rsp(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason); int sig_pri_cc_agent_status_req(struct ast_cc_agent *agent); int sig_pri_cc_agent_stop_ringing(struct ast_cc_agent *agent); int sig_pri_cc_agent_party_b_free(struct ast_cc_agent *agent); diff --git a/include/asterisk/ccss.h b/include/asterisk/ccss.h index 31073a4781..ae85e044f4 100644 --- a/include/asterisk/ccss.h +++ b/include/asterisk/ccss.h @@ -858,6 +858,15 @@ struct ast_cc_agent { char device_name[1]; }; +enum ast_cc_agent_response_reason { + /*! CC request accepted */ + AST_CC_AGENT_RESPONSE_SUCCESS, + /*! CC request not allowed at this time. Invalid state transition. */ + AST_CC_AGENT_RESPONSE_FAILURE_INVALID, + /*! Too many CC requests in the system. */ + AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY, +}; + struct ast_cc_agent_callbacks { /*! * \brief Type of agent the callbacks belong to. @@ -920,19 +929,23 @@ struct ast_cc_agent_callbacks { */ int (*stop_offer_timer)(struct ast_cc_agent *agent); /*! - * \brief Acknowledge CC request. + * \brief Respond to a CC request. * * \param agent CC core agent control. + * \param reason CC request response status. * * \details * When the core receives knowledge that a called * party has accepted a CC request, it will call - * this callback. + * this callback. The core may also call this + * if there is some error when attempting to process + * the incoming CC request. * - * The duty of this is to accept a CC request from - * the caller by acknowledging receipt of that request. + * The duty of this is to issue a propper response to a + * CC request from the caller by acknowledging receipt + * of that request or rejecting it. */ - void (*ack)(struct ast_cc_agent *agent); + void (*respond)(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason); /*! * \brief Request the status of the agent's device. * diff --git a/main/ccss.c b/main/ccss.c index 45f3671c3e..2cb14d5b15 100644 --- a/main/ccss.c +++ b/main/ccss.c @@ -2205,7 +2205,7 @@ static void check_callback_sanity(const struct ast_cc_agent_callbacks *callbacks ast_assert(callbacks->init != NULL); ast_assert(callbacks->start_offer_timer != NULL); ast_assert(callbacks->stop_offer_timer != NULL); - ast_assert(callbacks->ack != NULL); + ast_assert(callbacks->respond != NULL); ast_assert(callbacks->status_request != NULL); ast_assert(callbacks->start_monitoring != NULL); ast_assert(callbacks->callee_available != NULL); @@ -2267,7 +2267,7 @@ static struct ast_cc_agent *cc_agent_init(struct ast_channel *caller_chan, static int cc_generic_agent_init(struct ast_cc_agent *agent, struct ast_channel *chan); static int cc_generic_agent_start_offer_timer(struct ast_cc_agent *agent); static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent); -static void cc_generic_agent_ack(struct ast_cc_agent *agent); +static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason); static int cc_generic_agent_status_request(struct ast_cc_agent *agent); static int cc_generic_agent_stop_ringing(struct ast_cc_agent *agent); static int cc_generic_agent_start_monitoring(struct ast_cc_agent *agent); @@ -2279,7 +2279,7 @@ static struct ast_cc_agent_callbacks generic_agent_callbacks = { .init = cc_generic_agent_init, .start_offer_timer = cc_generic_agent_start_offer_timer, .stop_offer_timer = cc_generic_agent_stop_offer_timer, - .ack = cc_generic_agent_ack, + .respond = cc_generic_agent_respond, .status_request = cc_generic_agent_status_request, .stop_ringing = cc_generic_agent_stop_ringing, .start_monitoring = cc_generic_agent_start_monitoring, @@ -2403,7 +2403,7 @@ static int cc_generic_agent_stop_offer_timer(struct ast_cc_agent *agent) return 0; } -static void cc_generic_agent_ack(struct ast_cc_agent *agent) +static void cc_generic_agent_respond(struct ast_cc_agent *agent, enum ast_cc_agent_response_reason reason) { /* The generic agent doesn't have to do anything special to * acknowledge a CC request. Just return. @@ -2635,6 +2635,7 @@ static struct cc_core_instance *cc_core_init_instance(struct ast_channel *caller } struct cc_state_change_args { + struct cc_core_instance *core_instance;/*!< Holds reference to core instance. */ enum cc_state state; int core_id; char debug[1]; @@ -2783,6 +2784,8 @@ static int cc_caller_requested(struct cc_core_instance *core_instance, struct cc { if (!ast_cc_request_is_within_limits()) { ast_log(LOG_WARNING, "Cannot request CC since there is no more room for requests\n"); + core_instance->agent->callbacks->respond(core_instance->agent, + AST_CC_AGENT_RESPONSE_FAILURE_TOO_MANY); ast_cc_failed(core_instance->core_id, "Too many requests in the system"); return -1; } @@ -2821,7 +2824,8 @@ static int cc_active(struct cc_core_instance *core_instance, struct cc_state_cha * call monitor's unsuspend callback. */ if (previous_state == CC_CALLER_REQUESTED) { - core_instance->agent->callbacks->ack(core_instance->agent); + core_instance->agent->callbacks->respond(core_instance->agent, + AST_CC_AGENT_RESPONSE_SUCCESS); manager_event(EVENT_FLAG_CC, "CCRequestAcknowledged", "CoreID: %d\r\n" "Caller: %s\r\n", @@ -2958,15 +2962,19 @@ static int cc_do_state_change(void *datap) ast_log_dynamic_level(cc_logger_level, "Core %d: State change to %d requested. Reason: %s\n", args->core_id, args->state, args->debug); - if (!(core_instance = find_cc_core_instance(args->core_id))) { - ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n", args->core_id); - ast_free(args); - return -1; - } + core_instance = args->core_instance; if (!is_state_change_valid(core_instance->current_state, args->state, core_instance->agent)) { ast_log_dynamic_level(cc_logger_level, "Core %d: Invalid state change requested. Cannot go from %s to %s\n", args->core_id, cc_state_to_string(core_instance->current_state), cc_state_to_string(args->state)); + if (args->state == CC_CALLER_REQUESTED) { + /* + * For out-of-order requests, we need to let the requester know that + * we can't handle the request now. + */ + core_instance->agent->callbacks->respond(core_instance->agent, + AST_CC_AGENT_RESPONSE_FAILURE_INVALID); + } ast_free(args); cc_unref(core_instance, "Unref core instance from when it was found earlier"); return -1; @@ -2988,6 +2996,7 @@ static int cc_request_state_change(enum cc_state state, const int core_id, const int debuglen; char dummy[1]; va_list aq; + struct cc_core_instance *core_instance; struct cc_state_change_args *args; /* This initial call to vsnprintf is simply to find what the * size of the string needs to be @@ -3003,12 +3012,22 @@ static int cc_request_state_change(enum cc_state state, const int core_id, const return -1; } + core_instance = find_cc_core_instance(core_id); + if (!core_instance) { + ast_log_dynamic_level(cc_logger_level, "Core %d: Unable to find core instance.\n", + core_id); + ast_free(args); + return -1; + } + + args->core_instance = core_instance; args->state = state; args->core_id = core_id; vsnprintf(args->debug, debuglen, debug, ap); res = ast_taskprocessor_push(cc_core_taskprocessor, cc_do_state_change, args); if (res) { + cc_unref(core_instance, "Unref core instance. ast_taskprocessor_push failed"); ast_free(args); } return res; -- GitLab