summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2015-04-15 10:38:02 -0500
committerMatt Jordan <mjordan@digium.com>2015-04-17 15:57:10 -0500
commit4f1a8dbe9241cf967000d9f5f359830948da3c9a (patch)
treea2056f9e99a4bc0272ec4137ea26a1efdc0726b1 /apps
parentb56c1914fa54817e88be92ed8394aa6f64aa26ad (diff)
Detect potential forwarding loops based on count.
A potential problem that can arise is the following: * Bob's phone is programmed to automatically forward to Carol. * Carol's phone is programmed to automatically forward to Bob. * Alice calls Bob. If left unchecked, this results in an endless loops of call forwards that would eventually result in some sort of fiery crash. Asterisk's method of solving this issue was to track which interfaces had been dialed. If a destination were dialed a second time, then the attempt to call that destination would fail since a loop was detected. The problem with this method is that call forwarding has evolved. Some SIP phones allow for a user to manually forward an incoming call to an ad-hoc destination. This can mean that: * There are legitimate use cases where a device may be dialed multiple times, or * There can be human error when forwarding calls. This change removes the old method of detecting forwarding loops in favor of keeping a count of the number of destinations a channel has dialed on a particular branch of a call. If the number exceeds the set number of max forwards, then the call fails. This approach has the following advantages over the old: * It is much simpler. * It can detect loops involving local channels. * It is user configurable. The only disadvantage it has is that in the case where there is a legitimate forwarding loop present, it takes longer to detect it. However, the forwarding loop is still properly detected and the call is cleaned up as it should be. Address review feedback on gerrit. * Correct "mfgium" to "Digium" * Decrement max forwards by one in the case where allocation of the max forwards datastore is required. * Remove irrelevant code change from pjsip_global_headers.c ASTERISK-24958 #close Change-Id: Ia7e4b7cd3bccfbd34d9a859838356931bba56c23
Diffstat (limited to 'apps')
-rw-r--r--apps/app_dial.c85
-rw-r--r--apps/app_followme.c13
-rw-r--r--apps/app_queue.c96
3 files changed, 40 insertions, 154 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index ff18f959b..b6c07514d 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -58,7 +58,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
-#include "asterisk/global_datastores.h"
#include "asterisk/dsp.h"
#include "asterisk/aoc.h"
#include "asterisk/ccss.h"
@@ -68,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis_channels.h"
#include "asterisk/bridge_after.h"
#include "asterisk/features_config.h"
+#include "asterisk/max_forwards.h"
/*** DOCUMENTATION
<application name="Dial" language="en_US">
@@ -881,6 +881,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
ast_channel_lock_both(in, o->chan);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, o->chan);
+ ast_max_forwards_decrement(o->chan);
ast_channel_unlock(in);
ast_channel_unlock(o->chan);
/* When a call is forwarded, we don't want to track new interfaces
@@ -2074,7 +2075,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
);
struct ast_flags64 opts = { 0, };
char *opt_args[OPT_ARG_ARRAY_SIZE];
- struct ast_datastore *datastore = NULL;
int fulldial = 0, num_dialed = 0;
int ignore_cc = 0;
char device_name[AST_CHANNEL_NAME];
@@ -2101,6 +2101,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
* \note This will not have any malloced strings so do not free it.
*/
struct ast_party_caller caller;
+ int max_forwards;
/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
ast_channel_lock(chan);
@@ -2111,8 +2112,16 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
ast_channel_stage_snapshot_done(chan);
+ max_forwards = ast_max_forwards_get(chan);
ast_channel_unlock(chan);
+ if (max_forwards <= 0) {
+ ast_log(LOG_WARNING, "Cannot place outbound call from channel '%s'. Max forwards exceeded\n",
+ ast_channel_name(chan));
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", "BUSY");
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
@@ -2314,9 +2323,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
char *tech = strsep(&number, "/");
size_t tech_len;
size_t number_len;
- /* find if we already dialed this interface */
- struct ast_dialed_interface *di;
- AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
num_dialed++;
if (ast_strlen_zero(number)) {
@@ -2360,7 +2366,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
/* Request the peer */
ast_channel_lock(chan);
- datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL);
/*
* Seed the chanlist's connected line information with previously
* acquired connected line info from the incoming channel. The
@@ -2370,61 +2375,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
ast_channel_unlock(chan);
- if (datastore)
- dialed_interfaces = datastore->data;
- else {
- if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
- ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
- chanlist_free(tmp);
- goto out;
- }
- datastore->inheritance = DATASTORE_INHERIT_FOREVER;
-
- if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
- ast_datastore_free(datastore);
- chanlist_free(tmp);
- goto out;
- }
-
- datastore->data = dialed_interfaces;
- AST_LIST_HEAD_INIT(dialed_interfaces);
-
- ast_channel_lock(chan);
- ast_channel_datastore_add(chan, datastore);
- ast_channel_unlock(chan);
- }
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
- if (!strcasecmp(di->interface, tmp->interface)) {
- ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
- di->interface);
- break;
- }
- }
- AST_LIST_UNLOCK(dialed_interfaces);
- if (di) {
- fulldial++;
- chanlist_free(tmp);
- continue;
- }
-
- /* It is always ok to dial a Local interface. We only keep track of
- * which "real" interfaces have been dialed. The Local channel will
- * inherit this list so that if it ends up dialing a real interface,
- * it won't call one that has already been called. */
- if (strcasecmp(tmp->tech, "Local")) {
- if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) {
- chanlist_free(tmp);
- goto out;
- }
- strcpy(di->interface, tmp->interface);
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
- AST_LIST_UNLOCK(dialed_interfaces);
- }
-
tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), NULL, chan, tmp->number, &cause);
if (!tc) {
/* If we can't, just go on to the next call */
@@ -2465,6 +2415,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables(chan, tc);
ast_channel_datastore_inherit(chan, tc);
+ ast_max_forwards_decrement(tc);
ast_channel_appl_set(tc, "AppDial");
ast_channel_data_set(tc, "(Outgoing Line)");
@@ -2680,18 +2631,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
dtmf_progress, ignore_cc, &forced_clid, &stored_clid);
- /* The ast_channel_datastore_remove() function could fail here if the
- * datastore was moved to another channel during a masquerade. If this is
- * the case, don't free the datastore here because later, when the channel
- * to which the datastore was moved hangs up, it will attempt to free this
- * datastore again, causing a crash
- */
- ast_channel_lock(chan);
- datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* make sure we weren't cleaned up already */
- if (datastore && !ast_channel_datastore_remove(chan, datastore)) {
- ast_datastore_free(datastore);
- }
- ast_channel_unlock(chan);
if (!peer) {
if (result) {
res = result;
diff --git a/apps/app_followme.c b/apps/app_followme.c
index 8531b17b7..e5a5ee3c5 100644
--- a/apps/app_followme.c
+++ b/apps/app_followme.c
@@ -64,6 +64,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dsp.h"
#include "asterisk/app.h"
#include "asterisk/stasis_channels.h"
+#include "asterisk/max_forwards.h"
/*** DOCUMENTATION
<application name="FollowMe" language="en_US">
@@ -1069,6 +1070,7 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller));
ast_channel_inherit_variables(caller, outbound);
ast_channel_datastore_inherit(caller, outbound);
+ ast_max_forwards_decrement(outbound);
ast_channel_language_set(outbound, ast_channel_language(caller));
ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
@@ -1304,12 +1306,23 @@ static int app_exec(struct ast_channel *chan, const char *data)
AST_APP_ARG(options);
);
char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
+ int max_forwards;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
return -1;
}
+ ast_channel_lock(chan);
+ max_forwards = ast_max_forwards_get(chan);
+ ast_channel_unlock(chan);
+
+ if (max_forwards <= 0) {
+ ast_log(LOG_WARNING, "Unable to execute FollowMe on channel %s. Max forwards exceeded\n",
+ ast_channel_name(chan));
+ return -1;
+ }
+
argstr = ast_strdupa((char *) data);
AST_STANDARD_APP_ARGS(args, argstr);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 6d45be370..df5ff306e 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -98,7 +98,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
-#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/aoc.h"
#include "asterisk/callerid.h"
@@ -113,6 +112,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/mixmonitor.h"
#include "asterisk/core_unreal.h"
#include "asterisk/bridge_basic.h"
+#include "asterisk/max_forwards.h"
/*!
* \par Please read before modifying this file.
@@ -4260,6 +4260,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables(qe->chan, tmp->chan);
ast_channel_datastore_inherit(qe->chan, tmp->chan);
+ ast_max_forwards_decrement(tmp->chan);
/* Presense of ADSI CPE on outgoing channel follows ours */
ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan));
@@ -4753,6 +4754,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_channel_lock_both(o->chan, in);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, o->chan);
+ ast_max_forwards_decrement(o->chan);
if (o->pending_connected_update) {
/*
@@ -6234,10 +6236,7 @@ static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
*
* Here is the process of this function
* 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
- * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
- * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
- * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
- * during each iteration, we call calc_metric to determine which members should be rung when.
+ * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
* 3. Call ring_one to place a call to the appropriate member(s)
* 4. Call wait_for_answer to wait for an answer. If no one answers, return.
* 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
@@ -6290,13 +6289,8 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
int block_connected_line = 0;
int callcompletedinsl;
struct ao2_iterator memi;
- struct ast_datastore *datastore;
struct queue_end_bridge *queue_end_bridge = NULL;
- ast_channel_lock(qe->chan);
- datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
- ast_channel_unlock(qe->chan);
-
memset(&bridge_config, 0, sizeof(bridge_config));
tmpid[0] = 0;
time(&now);
@@ -6383,73 +6377,12 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
memi = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&memi))) {
struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
- struct ast_dialed_interface *di;
- AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
if (!tmp) {
ao2_ref(cur, -1);
ao2_iterator_destroy(&memi);
ao2_unlock(qe->parent);
goto out;
}
- if (!datastore) {
- if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
- callattempt_free(tmp);
- ao2_ref(cur, -1);
- ao2_iterator_destroy(&memi);
- ao2_unlock(qe->parent);
- goto out;
- }
- datastore->inheritance = DATASTORE_INHERIT_FOREVER;
- if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
- callattempt_free(tmp);
- ao2_ref(cur, -1);
- ao2_iterator_destroy(&memi);
- ao2_unlock(qe->parent);
- goto out;
- }
- datastore->data = dialed_interfaces;
- AST_LIST_HEAD_INIT(dialed_interfaces);
-
- ast_channel_lock(qe->chan);
- ast_channel_datastore_add(qe->chan, datastore);
- ast_channel_unlock(qe->chan);
- } else
- dialed_interfaces = datastore->data;
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
- if (!strcasecmp(cur->interface, di->interface)) {
- ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
- di->interface);
- break;
- }
- }
- AST_LIST_UNLOCK(dialed_interfaces);
-
- if (di) {
- callattempt_free(tmp);
- ao2_ref(cur, -1);
- continue;
- }
-
- /* It is always ok to dial a Local interface. We only keep track of
- * which "real" interfaces have been dialed. The Local channel will
- * inherit this list so that if it ends up dialing a real interface,
- * it won't call one that has already been called. */
- if (strncasecmp(cur->interface, "Local/", 6)) {
- if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
- callattempt_free(tmp);
- ao2_ref(cur, -1);
- ao2_iterator_destroy(&memi);
- ao2_unlock(qe->parent);
- goto out;
- }
- strcpy(di->interface, cur->interface);
-
- AST_LIST_LOCK(dialed_interfaces);
- AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
- AST_LIST_UNLOCK(dialed_interfaces);
- }
/*
* Seed the callattempt's connected line information with previously
@@ -6508,16 +6441,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
forwardsallowed, ringing);
- /* The ast_channel_datastore_remove() function could fail here if the
- * datastore was moved to another channel during a masquerade. If this is
- * the case, don't free the datastore here because later, when the channel
- * to which the datastore was moved hangs up, it will attempt to free this
- * datastore again, causing a crash
- */
- ast_channel_lock(qe->chan);
- if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
- ast_datastore_free(datastore);
- }
+
ast_channel_unlock(qe->chan);
ao2_lock(qe->parent);
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
@@ -7709,12 +7633,22 @@ static int queue_exec(struct ast_channel *chan, const char *data)
struct queue_ent qe = { 0 };
struct ast_flags opts = { 0, };
char *opt_args[OPT_ARG_ARRAY_SIZE];
+ int max_forwards;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
return -1;
}
+ ast_channel_lock(chan);
+ max_forwards = ast_max_forwards_get(chan);
+ ast_channel_unlock(chan);
+
+ if (max_forwards <= 0) {
+ ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
+ return -1;
+ }
+
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);