]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_dial: Properly handle callee hangup while sending digits. master
authorNaveen Albert <asterisk@phreaknet.org>
Wed, 6 May 2026 01:52:18 +0000 (21:52 -0400)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Tue, 12 May 2026 16:31:20 +0000 (16:31 +0000)
If we are sending digits (either DTMF, MF, or SF) to the called channel
after receiving progress or a wink, and the callee hangs up before we
have finished sending it digits, there are several problems that can ensue:

* If the callee hung up without answering, the calling channel would
  hang up and not continue in the dialplan.
* If the callee *did* answer before hanging up, the answer was never
  passed through to the caller, since this gets "eaten" by the various
  digit streaming functions and is never processed by app_dial.

This is generally an edge case that occurs due to some kind of signaling
failure, but to better handle this:

* Set to_answer to 0 to prevent hangup on the exit path, just like other
  parts of wait_for_answer.
* Better document this usage of to_answer.
* If the channel did answer while it was receiving digits, manually
  answer the calling channel before we abort. The call would not continue
  in the dialplan anyways (either before or after this fix), but technically
  the call was answered, so the CDRs should probably reflect that, and this
  mirrors the behavior of calls which normally do not continue.

Resolves: #1915

UserNote: If a called channel sends progress or wink and the caller begins
sending digits but the callee answers and then hangs up before digit
sending can finish, the call is now answered before being disconnected.
If the callee hangs up without answering, the call now continues in
the dialplan.

apps/app_dial.c

index a6c91b2ffbbfe8c2ec00bdd039d6064da20b388e..e3b051e389a84eb88de7b1d16e83debd767294f7 100644 (file)
@@ -1291,7 +1291,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                        } else {
                                ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
                        }
-                       *to_answer = 0;
+                       *to_answer = 0; /* Continue in the dialplan, since nobody answered */
                        if (is_cc_recall) {
                                ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
                        }
@@ -1603,6 +1603,17 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                }
                                                if (res) {
                                                        ast_log(LOG_WARNING, "Called channel %s hung up post-progress before all digits could be sent\n", ast_channel_name(c));
+                                                       if (ast_channel_state(c) == AST_STATE_UP) {
+                                                               /* The called channel answered while we were sending it digits, so the answer never got processed by app_dial.
+                                                                * The channel is dying now, but better to answer late than never? */
+                                                               ast_debug(1, "Channel %s answered while we were sending it digits, answering %s retroactively\n", ast_channel_name(c), ast_channel_name(in));
+                                                               /* Indicate answer supervision to the caller before we exit.
+                                                                * We're not going to bridge, but this way at least the CDRs are correct, etc. */
+                                                               ast_raw_answer(in);
+                                                               strcpy(pa->status, "ANSWER");
+                                                       } else {
+                                                               *to_answer = 0; /* Continue in the dialplan, since nobody answered */
+                                                       }
                                                        goto wait_over;
                                                }
                                        }
@@ -1630,6 +1641,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                }
                                                if (res) {
                                                        ast_log(LOG_WARNING, "Called channel %s hung up post-wink before all digits could be sent\n", ast_channel_name(c));
+                                                       if (ast_channel_state(c) == AST_STATE_UP) {
+                                                               /* Same as in AST_CONTROL_PROGRESS */
+                                                               ast_debug(1, "Channel %s answered while we were sending it digits, answering %s retroactively\n", ast_channel_name(c), ast_channel_name(in));
+                                                               ast_raw_answer(in);
+                                                               strcpy(pa->status, "ANSWER");
+                                                       } else {
+                                                               *to_answer = 0; /* Continue in the dialplan, since nobody answered */
+                                                       }
                                                        goto wait_over;
                                                }
                                        }
@@ -1937,7 +1956,11 @@ skip_frame:;
 
 wait_over:
        if (!*to_answer || ast_check_hangup(in)) {
-               ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
+               if (orig_answer_to != -1) {
+                       ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
+               } else {
+                       ast_verb(3, "Call terminated without answer\n");
+               }
                publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
        } else if (!*to_progress) {
                ast_verb(3, "No early media received in %d ms\n", orig_progress_to);
@@ -2998,8 +3021,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
        if (!peer) {
                if (result) {
-                       res = result;
+                       res = result; /* User entered a DTMF digit that matched a context */
                } else if (to_answer) { /* Musta gotten hung up */
+                       /* This does not necessarily mean that we dialed without a timeout.
+                        * to_answer is (ab)used by wait_for_answer to to indicate whether or we should continue in the dialplan or exit. */
                        res = -1;
                } else { /* Nobody answered, next please? */
                        res = 0;