summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2017-11-16 15:04:55 +0000
committerJoshua Colp <jcolp@digium.com>2017-11-17 12:42:25 +0000
commit0a7bbb068b58482882a81b873825917ac968921d (patch)
treec974aa8dafa9dfccd130a5ba858a32f82fba5aa7
parente25ec49b629e516882faf70dfafb687f39d58cf0 (diff)
bridge_basic: Ignore answer from transfer target when they've timed out.
This is a fun one. Given the following attended transfer scenario: 1. Transfer target is called 2. Transferer hangs up 3. Transfer target call attempt reaches timeout 4. Transfer target is told to hang up 5. Transfer target answers before channel is hung up 6. Transferer recall target is called A crash would occur. This is because the transfer target call attempt, despite being told to hang up, would raise a recall target answer before the recall target had been answered. As it had not answered there would be no recall target channel and it would implode. This change makes it so that if the transfer target has been hung up we don't tell the attended transfer code that it has answered. We also clear out the stimulus that the recall target has been answered after telling the transfer target to hang up, in case it was able to raise the information before we told it to hangup. ASTERISK-27361 Change-Id: Ifb8b255a9c4d2c5c1b8ad77bf54f659ed286df99
-rw-r--r--main/bridge_basic.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index b24df0506..ef6d47cfe 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -1549,6 +1549,23 @@ static void stimulate_attended_transfer(struct attended_transfer_properties *pro
ao2_unlock(props);
}
+static void remove_attended_transfer_stimulus(struct attended_transfer_properties *props,
+ enum attended_transfer_stimulus stimulus)
+{
+ struct stimulus_list *list;
+
+ ao2_lock(props);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&props->stimulus_queue, list, next) {
+ if (list->stimulus == stimulus) {
+ AST_LIST_REMOVE_CURRENT(next);
+ ast_free(list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ao2_unlock(props);
+}
+
/*!
* \brief Get a desired transfer party for a bridge the transferer is not in.
*
@@ -2341,6 +2358,10 @@ static enum attended_transfer_state blond_nonfinal_exit(struct attended_transfer
return TRANSFER_RESUME;
case STIMULUS_TIMEOUT:
ast_softhangup(props->recall_target, AST_SOFTHANGUP_EXPLICIT);
+ /* It is possible before we hung them up that they queued up a recall target answer
+ * so we remove it if present as it should not exist.
+ */
+ remove_attended_transfer_stimulus(props, STIMULUS_RECALL_TARGET_ANSWER);
case STIMULUS_RECALL_TARGET_HANGUP:
props->recall_target = ast_channel_unref(props->recall_target);
return TRANSFER_RECALLING;
@@ -2806,7 +2827,8 @@ static struct ast_frame *transfer_target_framehook_cb(struct ast_channel *chan,
if (event == AST_FRAMEHOOK_EVENT_READ &&
frame && frame->frametype == AST_FRAME_CONTROL &&
- frame->subclass.integer == AST_CONTROL_ANSWER) {
+ frame->subclass.integer == AST_CONTROL_ANSWER &&
+ !ast_check_hangup(chan)) {
ast_debug(1, "Detected an answer for recall attempt on attended transfer %p\n", props);
if (props->superstate == SUPERSTATE_TRANSFER) {