summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_musiconhold.c156
-rw-r--r--res/res_odbc.c70
-rw-r--r--res/res_pjsip.c22
-rw-r--r--res/res_pjsip/pjsip_configuration.c40
-rw-r--r--res/res_pjsip/pjsip_distributor.c242
-rw-r--r--res/res_pjsip_sdp_rtp.c19
-rw-r--r--res/res_rtp_asterisk.c38
7 files changed, 402 insertions, 185 deletions
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index be50e9cee..e4bb7a2d9 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -159,6 +159,11 @@ struct moh_files_state {
static struct ast_flags global_flags[1] = {{0}}; /*!< global MOH_ flags */
+enum kill_methods {
+ KILL_METHOD_PROCESS_GROUP = 0,
+ KILL_METHOD_PROCESS
+};
+
struct mohclass {
char name[MAX_MUSICCLASS];
char dir[256];
@@ -179,6 +184,10 @@ struct mohclass {
int pid;
time_t start;
pthread_t thread;
+ /*! Millisecond delay between kill attempts */
+ size_t kill_delay;
+ /*! Kill method */
+ enum kill_methods kill_method;
/*! Source of audio */
int srcfd;
/*! Generic timer */
@@ -680,6 +689,51 @@ static int spawn_mp3(struct mohclass *class)
return fds[0];
}
+static int killer(pid_t pid, int signum, enum kill_methods kill_method)
+{
+ switch (kill_method) {
+ case KILL_METHOD_PROCESS_GROUP:
+ return killpg(pid, signum);
+ case KILL_METHOD_PROCESS:
+ return kill(pid, signum);
+ }
+
+ return -1;
+}
+
+static void killpid(int pid, size_t delay, enum kill_methods kill_method)
+{
+ if (killer(pid, SIGHUP, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent HUP to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+ usleep(delay);
+ if (killer(pid, SIGTERM, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to terminate MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent TERM to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+ usleep(delay);
+ if (killer(pid, SIGKILL, kill_method) < 0) {
+ if (errno == ESRCH) {
+ return;
+ }
+ ast_log(LOG_WARNING, "Unable to kill MOH process '%d'?!!: %s\n", pid, strerror(errno));
+ } else {
+ ast_debug(1, "Sent KILL to pid %d%s\n", pid,
+ kill_method == KILL_METHOD_PROCESS_GROUP ? " and all children" : " only");
+ }
+}
+
static void *monmp3thread(void *data)
{
#define MOH_MS_INTERVAL 100
@@ -755,28 +809,7 @@ static void *monmp3thread(void *data)
class->srcfd = -1;
pthread_testcancel();
if (class->pid > 1) {
- do {
- if (killpg(class->pid, SIGHUP) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(class->pid, SIGTERM) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(class->pid, SIGKILL) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
- }
- } while (0);
+ killpid(class->pid, class->kill_delay, class->kill_method);
class->pid = 0;
}
} else {
@@ -1357,6 +1390,7 @@ static struct mohclass *_moh_class_malloc(const char *file, int line, const char
if (class) {
class->format = ao2_bump(ast_format_slin);
class->srcfd = -1;
+ class->kill_delay = 100000;
}
return class;
@@ -1610,44 +1644,22 @@ static void moh_class_destructor(void *obj)
if (class->pid > 1) {
char buff[8192];
- int bytes, tbytes = 0, stime = 0, pid = 0;
+ int bytes, tbytes = 0, stime = 0;
ast_debug(1, "killing %d!\n", class->pid);
stime = time(NULL) + 2;
- pid = class->pid;
- class->pid = 0;
-
- /* Back when this was just mpg123, SIGKILL was fine. Now we need
- * to give the process a reason and time enough to kill off its
- * children. */
- do {
- if (killpg(pid, SIGHUP) < 0) {
- ast_log(LOG_WARNING, "Unable to send a SIGHUP to MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(pid, SIGTERM) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to terminate MOH process?!!: %s\n", strerror(errno));
- }
- usleep(100000);
- if (killpg(pid, SIGKILL) < 0) {
- if (errno == ESRCH) {
- break;
- }
- ast_log(LOG_WARNING, "Unable to kill MOH process?!!: %s\n", strerror(errno));
- }
- } while (0);
+ killpid(class->pid, class->kill_delay, class->kill_method);
while ((ast_wait_for_input(class->srcfd, 100) > 0) &&
(bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
tbytes = tbytes + bytes;
}
- ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
+ ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n",
+ class->pid, tbytes);
+ class->pid = 0;
close(class->srcfd);
class->srcfd = -1;
}
@@ -1752,6 +1764,49 @@ static int load_moh_classes(int reload)
/* For compatibility with the past, we overwrite any name=name
* with the context [name]. */
ast_copy_string(class->name, cat, sizeof(class->name));
+ for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
+ if (!strcasecmp(var->name, "mode")) {
+ ast_copy_string(class->mode, var->value, sizeof(class->mode));
+ } else if (!strcasecmp(var->name, "directory")) {
+ ast_copy_string(class->dir, var->value, sizeof(class->dir));
+ } else if (!strcasecmp(var->name, "application")) {
+ ast_copy_string(class->args, var->value, sizeof(class->args));
+ } else if (!strcasecmp(var->name, "announcement")) {
+ ast_copy_string(class->announcement, var->value, sizeof(class->announcement));
+ ast_set_flag(class, MOH_ANNOUNCEMENT);
+ } else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value))) {
+ class->digit = *var->value;
+ } else if (!strcasecmp(var->name, "random")) {
+ ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
+ } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random")) {
+ ast_set_flag(class, MOH_RANDOMIZE);
+ } else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) {
+ ast_set_flag(class, MOH_SORTALPHA);
+ } else if (!strcasecmp(var->name, "format")) {
+ ao2_cleanup(class->format);
+ class->format = ast_format_cache_get(var->value);
+ if (!class->format) {
+ ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
+ class->format = ao2_bump(ast_format_slin);
+ }
+ } else if (!strcasecmp(var->name, "kill_escalation_delay")) {
+ if (sscanf(var->value, "%zu", &class->kill_delay) == 1) {
+ class->kill_delay *= 1000;
+ } else {
+ ast_log(LOG_WARNING, "kill_escalation_delay '%s' is invalid. Setting to 100ms\n", var->value);
+ class->kill_delay = 100000;
+ }
+ } else if (!strcasecmp(var->name, "kill_method")) {
+ if (!strcasecmp(var->value, "process")) {
+ class->kill_method = KILL_METHOD_PROCESS;
+ } else if (!strcasecmp(var->value, "process_group")){
+ class->kill_method = KILL_METHOD_PROCESS_GROUP;
+ } else {
+ ast_log(LOG_WARNING, "kill_method '%s' is invalid. Setting to 'process_group'\n", var->value);
+ class->kill_method = KILL_METHOD_PROCESS_GROUP;
+ }
+ }
+ }
if (ast_strlen_zero(class->dir)) {
if (!strcasecmp(class->mode, "custom")) {
@@ -1884,6 +1939,9 @@ static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struc
ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
if (ast_test_flag(class, MOH_CUSTOM)) {
ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
+ ast_cli(a->fd, "\tKill Escalation Delay: %zu ms\n", class->kill_delay / 1000);
+ ast_cli(a->fd, "\tKill Method: %s\n",
+ class->kill_method == KILL_METHOD_PROCESS ? "process" : "process_group");
}
if (strcasecmp(class->mode, "files")) {
ast_cli(a->fd, "\tFormat: %s\n", ast_format_get_name(class->format));
diff --git a/res/res_odbc.c b/res/res_odbc.c
index 0b81bc639..24f63a92e 100644
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -61,7 +61,6 @@
#include "asterisk/app.h"
#include "asterisk/strings.h"
#include "asterisk/threadstorage.h"
-#include "asterisk/data.h"
struct odbc_class
{
@@ -119,15 +118,6 @@ struct odbc_txn_frame {
char name[0]; /*!< Name of this transaction ID */
};
-#define DATA_EXPORT_ODBC_CLASS(MEMBER) \
- MEMBER(odbc_class, name, AST_DATA_STRING) \
- MEMBER(odbc_class, dsn, AST_DATA_STRING) \
- MEMBER(odbc_class, username, AST_DATA_STRING) \
- MEMBER(odbc_class, password, AST_DATA_PASSWORD) \
- MEMBER(odbc_class, forcecommit, AST_DATA_BOOLEAN)
-
-AST_DATA_STRUCTURE(odbc_class, DATA_EXPORT_ODBC_CLASS);
-
const char *ast_odbc_isolation2text(int iso)
{
if (iso == SQL_TXN_READ_COMMITTED) {
@@ -971,65 +961,6 @@ static odbc_status odbc_obj_connect(struct odbc_obj *obj)
return ODBC_SUCCESS;
}
-/*!
- * \internal
- * \brief Implements the channels provider.
- */
-static int data_odbc_provider_handler(const struct ast_data_search *search,
- struct ast_data *root)
-{
- struct ao2_iterator aoi;
- struct odbc_class *class;
- struct ast_data *data_odbc_class, *data_odbc_connections;
- struct ast_data *enum_node;
-
- aoi = ao2_iterator_init(class_container, 0);
- while ((class = ao2_iterator_next(&aoi))) {
- data_odbc_class = ast_data_add_node(root, "class");
- if (!data_odbc_class) {
- ao2_ref(class, -1);
- continue;
- }
-
- ast_data_add_structure(odbc_class, data_odbc_class, class);
-
- data_odbc_connections = ast_data_add_node(data_odbc_class, "connections");
- if (!data_odbc_connections) {
- ao2_ref(class, -1);
- continue;
- }
-
- /* isolation */
- enum_node = ast_data_add_node(data_odbc_class, "isolation");
- if (!enum_node) {
- ao2_ref(class, -1);
- continue;
- }
- ast_data_add_int(enum_node, "value", class->isolation);
- ast_data_add_str(enum_node, "text", ast_odbc_isolation2text(class->isolation));
- ao2_ref(class, -1);
-
- if (!ast_data_search_match(search, data_odbc_class)) {
- ast_data_remove_node(root, data_odbc_class);
- }
- }
- ao2_iterator_destroy(&aoi);
- return 0;
-}
-
-/*!
- * \internal
- * \brief /asterisk/res/odbc/listprovider.
- */
-static const struct ast_data_handler odbc_provider = {
- .version = AST_DATA_HANDLER_VERSION,
- .get = data_odbc_provider_handler
-};
-
-static const struct ast_data_entry odbc_providers[] = {
- AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider),
-};
-
static int reload(void)
{
struct odbc_cache_tables *table;
@@ -1087,7 +1018,6 @@ static int load_module(void)
if (load_odbc_config() == -1)
return AST_MODULE_LOAD_DECLINE;
ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc));
- ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers));
ast_log(LOG_NOTICE, "res_odbc loaded.\n");
return 0;
}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index e717fdb40..6e389d5f3 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -193,11 +193,18 @@
<description>
<para>Method used when updating connected line information.</para>
<enumlist>
- <enum name="invite" />
+ <enum name="invite">
+ <para>When set to <literal>invite</literal>, check the remote's Allow header and
+ if UPDATE is allowed, send UPDATE instead of INVITE to avoid SDP
+ renegotiation. If UPDATE is not Allowed, send INVITE.</para>
+ </enum>
<enum name="reinvite">
<para>Alias for the <literal>invite</literal> value.</para>
</enum>
- <enum name="update" />
+ <enum name="update">
+ <para>If set to <literal>update</literal>, send UPDATE regardless of what the remote
+ Allows. </para>
+ </enum>
</enumlist>
</description>
</configOption>
@@ -229,6 +236,9 @@
<enum name="auto">
<para>DTMF is sent as RFC 4733 if the other side supports it or as INBAND if not.</para>
</enum>
+ <enum name="auto_info">
+ <para>DTMF is sent as RFC 4733 if the other side supports it or as SIP INFO if not.</para>
+ </enum>
</enumlist>
</description>
</configOption>
@@ -3081,6 +3091,14 @@ pjsip_dialog *ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint,
/* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */
pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri);
dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0);
+ if (!dlg->local.info->uri) {
+ ast_log(LOG_ERROR,
+ "Could not parse URI '%s' for endpoint '%s'\n",
+ dlg->local.info_str.ptr, ast_sorcery_object_get_id(endpoint));
+ dlg->sess_count--;
+ pjsip_dlg_terminate(dlg);
+ return NULL;
+ }
dlg->local.contact = pjsip_parse_hdr(dlg->pool, &HCONTACT, local_uri.ptr, local_uri.slen, NULL);
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 56a8419a8..ef3e05b06 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -371,6 +371,8 @@ static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var,
endpoint->dtmf = AST_SIP_DTMF_RFC_4733;
} else if (!strcasecmp(var->value, "inband")) {
endpoint->dtmf = AST_SIP_DTMF_INBAND;
+ } else if (!strcasecmp(var->value, "auto_info")) {
+ endpoint->dtmf = AST_SIP_DTMF_AUTO_INFO;
} else if (!strcasecmp(var->value, "info")) {
endpoint->dtmf = AST_SIP_DTMF_INFO;
} else if (!strcasecmp(var->value, "auto")) {
@@ -395,8 +397,11 @@ static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
*buf = "inband"; break;
case AST_SIP_DTMF_INFO :
*buf = "info"; break;
- case AST_SIP_DTMF_AUTO :
+ case AST_SIP_DTMF_AUTO :
*buf = "auto"; break;
+ case AST_SIP_DTMF_AUTO_INFO :
+ *buf = "auto_info";
+ break;
default:
*buf = "none";
}
@@ -1143,6 +1148,37 @@ static int tos_video_to_str(const void *obj, const intptr_t *args, char **buf)
return 0;
}
+static int from_user_handler(const struct aco_option *opt,
+ struct ast_variable *var, void *obj)
+{
+ struct ast_sip_endpoint *endpoint = obj;
+ /* Valid non-alphanumeric characters for URI */
+ char *valid_uri_marks = "-_.!~*`()";
+ const char *val;
+
+ for (val = var->value; *val; val++) {
+ if (!strchr(valid_uri_marks, *val) && !isdigit(*val) && !isalpha(*val)) {
+ ast_log(LOG_ERROR, "Error configuring endpoint '%s' - '%s' field "
+ "contains invalid character '%c'\n",
+ ast_sorcery_object_get_id(endpoint), var->name, *val);
+ return -1;
+ }
+ }
+
+ ast_string_field_set(endpoint, fromuser, var->value);
+
+ return 0;
+}
+
+static int from_user_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+
+ *buf = ast_strdup(endpoint->fromuser);
+
+ return 0;
+}
+
static int set_var_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
@@ -1913,7 +1949,7 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sub_min_expiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
- ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "from_user", "", from_user_handler, from_user_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine));
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index cca26a83c..cf1b04a8b 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -150,62 +150,189 @@ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
/*! Dialog-specific information the distributor uses */
struct distributor_dialog_data {
+ /*! dialog_associations ao2 container key */
+ pjsip_dialog *dlg;
/*! Serializer to distribute tasks to for this dialog */
struct ast_taskprocessor *serializer;
/*! Endpoint associated with this dialog */
struct ast_sip_endpoint *endpoint;
};
+#define DIALOG_ASSOCIATIONS_BUCKETS 251
+
+static struct ao2_container *dialog_associations;
+
/*!
* \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
*
- * \note Call this with the dialog locked
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ * \param[in] hash The hash value to add to
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
*/
-static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg)
+static int buf_hash_add(const char *pos, size_t len, int hash)
{
- struct distributor_dialog_data *dist;
+ while (len--) {
+ hash = hash * 33 ^ *pos++;
+ }
+
+ return hash;
+}
+
+/*!
+ * \internal
+ * \brief Compute a hash value on an arbitrary buffer.
+ * \since 13.17.0
+ *
+ * \param[in] pos The buffer to add to the hash
+ * \param[in] len The buffer length to add to the hash
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * hash of more than one buffer.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
+ */
+static int buf_hash(const char *pos, size_t len)
+{
+ return buf_hash_add(pos, len, 5381);
+}
- dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data);
- pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist);
+static int dialog_associations_hash(const void *obj, int flags)
+{
+ const struct distributor_dialog_data *object;
+ union {
+ const pjsip_dialog *dlg;
+ const char buf[sizeof(pjsip_dialog *)];
+ } key;
- return dist;
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key.dlg = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ object = obj;
+ key.dlg = object->dlg;
+ break;
+ default:
+ /* Hash can only work on something with a full key. */
+ ast_assert(0);
+ return 0;
+ }
+ return ast_str_hash_restrict(buf_hash(key.buf, sizeof(key.buf)));
+}
+
+static int dialog_associations_cmp(void *obj, void *arg, int flags)
+{
+ const struct distributor_dialog_data *object_left = obj;
+ const struct distributor_dialog_data *object_right = arg;
+ const pjsip_dialog *right_key = arg;
+ int cmp = 0;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->dlg;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ if (object_left->dlg == right_key) {
+ cmp = CMP_MATCH;
+ }
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /* There is no such thing for this container. */
+ ast_assert(0);
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
}
void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ ao2_wrlock(dialog_associations);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (!dist) {
- dist = distributor_dialog_data_alloc(dlg);
+ if (serializer) {
+ dist = ao2_alloc(sizeof(*dist), NULL);
+ if (dist) {
+ dist->dlg = dlg;
+ dist->serializer = serializer;
+ ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+ ao2_ref(dist, -1);
+ }
+ }
+ } else {
+ ao2_lock(dist);
+ dist->serializer = serializer;
+ if (!dist->serializer && !dist->endpoint) {
+ ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+ }
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
}
- dist->serializer = serializer;
+ ao2_unlock(dialog_associations);
}
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ ao2_wrlock(dialog_associations);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (!dist) {
- dist = distributor_dialog_data_alloc(dlg);
+ if (endpoint) {
+ dist = ao2_alloc(sizeof(*dist), NULL);
+ if (dist) {
+ dist->dlg = dlg;
+ dist->endpoint = endpoint;
+ ao2_link_flags(dialog_associations, dist, OBJ_NOLOCK);
+ ao2_ref(dist, -1);
+ }
+ }
+ } else {
+ ao2_lock(dist);
+ dist->endpoint = endpoint;
+ if (!dist->serializer && !dist->endpoint) {
+ ao2_unlink_flags(dialog_associations, dist, OBJ_NOLOCK);
+ }
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
}
- dist->endpoint = endpoint;
+ ao2_unlock(dialog_associations);
}
struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
{
struct distributor_dialog_data *dist;
- SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock);
+ struct ast_sip_endpoint *endpoint;
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
- if (!dist || !dist->endpoint) {
- return NULL;
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
+ if (dist) {
+ ao2_lock(dist);
+ endpoint = ao2_bump(dist->endpoint);
+ ao2_unlock(dist);
+ ao2_ref(dist, -1);
+ } else {
+ endpoint = NULL;
}
- ao2_ref(dist->endpoint, +1);
- return dist->endpoint;
+ return endpoint;
}
static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
@@ -237,7 +364,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
rdata->msg_info.to->tag.slen != 0) {
dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
- remote_tag, PJ_TRUE);
+ remote_tag, PJ_FALSE);
if (dlg) {
return dlg;
}
@@ -275,11 +402,6 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
pj_mutex_unlock(tsx->mutex);
#endif
- if (!dlg) {
- return NULL;
- }
-
- pjsip_dlg_inc_lock(dlg);
return dlg;
}
@@ -302,16 +424,7 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
*/
static int pjstr_hash_add(pj_str_t *str, int hash)
{
- size_t len;
- const char *pos;
-
- len = pj_strlen(str);
- pos = pj_strbuf(str);
- while (len--) {
- hash = hash * 33 ^ *pos++;
- }
-
- return hash;
+ return buf_hash_add(pj_strbuf(str), pj_strlen(str), hash);
}
/*!
@@ -350,7 +463,7 @@ struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdat
/* Compute the hash from the SIP message call-id and remote-tag */
hash = pjstr_hash(&rdata->msg_info.cid->id);
hash = pjstr_hash_add(remote_tag, hash);
- hash = abs(hash);
+ hash = ast_str_hash_restrict(hash);
serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]);
if (serializer) {
@@ -385,17 +498,18 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
dlg = find_dialog(rdata);
if (dlg) {
- ast_debug(3, "Searching for serializer on dialog %s for %s\n",
+ ast_debug(3, "Searching for serializer associated with dialog %s for %s\n",
dlg->obj_name, pjsip_rx_data_get_info(rdata));
- dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
+ dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY);
if (dist) {
+ ao2_lock(dist);
serializer = ao2_bump(dist->serializer);
+ ao2_unlock(dist);
if (serializer) {
- ast_debug(3, "Found serializer %s on dialog %s\n",
+ ast_debug(3, "Found serializer %s associated with dialog %s\n",
ast_taskprocessor_name(serializer), dlg->obj_name);
}
}
- pjsip_dlg_dec_lock(dlg);
}
if (serializer) {
@@ -417,6 +531,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
/* We have a BYE or CANCEL request without a serializer. */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
+ ao2_cleanup(dist);
return PJ_TRUE;
} else {
if (ast_taskprocessor_alert_get()) {
@@ -431,6 +546,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
*/
ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n",
pjsip_rx_data_get_info(rdata));
+ ao2_cleanup(dist);
return PJ_TRUE;
}
@@ -438,10 +554,17 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
serializer = ast_sip_get_distributor_serializer(rdata);
}
- pjsip_rx_data_clone(rdata, 0, &clone);
+ if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) {
+ ast_taskprocessor_unreference(serializer);
+ ao2_cleanup(dist);
+ return PJ_TRUE;
+ }
if (dist) {
+ ao2_lock(dist);
clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
+ ao2_unlock(dist);
+ ao2_cleanup(dist);
}
if (ast_sip_push_task(serializer, distribute, clone)) {
@@ -837,7 +960,7 @@ static int suspects_compare(void *obj, void *arg, int flags)
/* Fall through */
case OBJ_SEARCH_KEY:
if (strcmp(object_left->src_name, right_key) == 0) {
- cmp = CMP_MATCH | CMP_STOP;
+ cmp = CMP_MATCH;
}
break;
case OBJ_SEARCH_PARTIAL_KEY:
@@ -852,15 +975,25 @@ static int suspects_compare(void *obj, void *arg, int flags)
return cmp;
}
-static int suspects_hash(const void *obj, int flags) {
- const struct unidentified_request *object_left = obj;
+static int suspects_hash(const void *obj, int flags)
+{
+ const struct unidentified_request *object;
+ const char *key;
- if (flags & OBJ_SEARCH_OBJECT) {
- return ast_str_hash(object_left->src_name);
- } else if (flags & OBJ_SEARCH_KEY) {
- return ast_str_hash(obj);
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ object = obj;
+ key = object->src_name;
+ break;
+ default:
+ /* Hash can only work on something with a full key. */
+ ast_assert(0);
+ return 0;
}
- return -1;
+ return ast_str_hash(key);
}
static struct ao2_container *cli_unid_get_container(const char *regex)
@@ -1078,6 +1211,14 @@ int ast_sip_initialize_distributor(void)
return -1;
}
+ dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
+ DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL,
+ dialog_associations_cmp);
+ if (!dialog_associations) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
if (distributor_pool_setup()) {
ast_sip_destroy_distributor();
return -1;
@@ -1156,5 +1297,6 @@ void ast_sip_destroy_distributor(void)
distributor_pool_shutdown();
+ ao2_cleanup(dialog_associations);
ao2_cleanup(unidentified_requests);
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 03fef40cf..a49130868 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -207,7 +207,7 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
ice->stop(session_media->rtp);
}
- if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) {
+ if (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) {
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_DTMF, 1);
} else if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
@@ -230,7 +230,7 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
}
static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs,
- struct ast_sip_session_media *session_media)
+ struct ast_sip_session_media *session_media)
{
pjmedia_sdp_attr *attr;
pjmedia_sdp_rtpmap *rtpmap;
@@ -296,6 +296,16 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
if (!tel_event && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
}
+
+ if (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) {
+ if (tel_event) {
+ ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_RFC2833);
+ } else {
+ ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_NONE);
+ }
+ }
+
+
/* Get the packetization, if it exists */
if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) {
unsigned long framing = pj_strtoul(pj_strltrim(&attr->value));
@@ -404,7 +414,8 @@ static int set_caps(struct ast_sip_session *session,
ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
}
- if ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO)
+
+ if ( ((session->endpoint->dtmf == AST_SIP_DTMF_AUTO) || (session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) )
&& (ast_rtp_instance_dtmf_mode_get(session_media->rtp) == AST_RTP_DTMF_MODE_RFC2833)
&& (session->dsp)) {
dsp_features = ast_dsp_get_features(session->dsp);
@@ -1136,7 +1147,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
pj_str_t stmp;
pjmedia_sdp_attr *attr;
int index = 0;
- int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO) ? AST_RTP_DTMF : 0;
+ int noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733 || session->endpoint->dtmf == AST_SIP_DTMF_AUTO || session->endpoint->dtmf == AST_SIP_DTMF_AUTO_INFO) ? AST_RTP_DTMF : 0;
int min_packet_size = 0, max_packet_size = 0;
int rtp_code;
RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index dc09166a7..30efb2ca6 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -319,6 +319,7 @@ struct ast_rtp {
ast_cond_t cond; /*!< ICE/TURN condition for signaling */
struct ice_wrap *ice; /*!< ao2 wrapped ICE session */
+ enum ast_rtp_ice_role role; /*!< Our role in ICE negotiation */
pj_turn_sock *turn_rtp; /*!< RTP TURN relay */
pj_turn_sock *turn_rtcp; /*!< RTCP TURN relay */
pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */
@@ -685,7 +686,6 @@ static void ice_wrap_dtor(void *vdoomed)
static int ice_reset_session(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
- pj_ice_sess_role role = rtp->ice->real_ice->role;
int res;
ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
@@ -697,8 +697,9 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
if (!res) {
- /* Preserve the role that the old ICE session used */
- pj_ice_sess_change_role(rtp->ice->real_ice, role);
+ /* Use the current expected role for the ICE session */
+ pj_ice_sess_change_role(rtp->ice->real_ice, rtp->role == AST_RTP_ICE_ROLE_CONTROLLED ?
+ PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
}
/* If we only have one component now, and we previously set up TURN for RTCP,
@@ -769,6 +770,8 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
ao2_cleanup(rtp->ice_proposed_remote_candidates);
rtp->ice_proposed_remote_candidates = NULL;
+ /* If this ICE session is being preserved then go back to the role it currently is */
+ rtp->role = rtp->ice->real_ice->role;
return;
}
@@ -942,10 +945,7 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
return;
}
- pj_thread_register_check();
-
- pj_ice_sess_change_role(rtp->ice->real_ice, role == AST_RTP_ICE_ROLE_CONTROLLED ?
- PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING);
+ rtp->role = role;
}
/*! \pre instance is locked */
@@ -1295,6 +1295,8 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
pj_turn_session_info info;
struct ast_sockaddr local, loop;
pj_status_t status;
+ pj_turn_sock_cfg turn_sock_cfg;
+ struct ice_wrap *ice;
ast_rtp_instance_get_local_address(instance, &local);
if (ast_sockaddr_is_ipv4(&local)) {
@@ -1357,11 +1359,20 @@ static void ast_rtp_ice_turn_request(struct ast_rtp_instance *instance, enum ast
pj_stun_config_init(&stun_config, &cachingpool.factory, 0, rtp->ioqueue->ioqueue, rtp->ioqueue->timerheap);
+ /* Use ICE session group lock for TURN session to avoid deadlock */
+ pj_turn_sock_cfg_default(&turn_sock_cfg);
+ ice = rtp->ice;
+ if (ice) {
+ turn_sock_cfg.grp_lock = ice->real_ice->grp_lock;
+ ao2_ref(ice, +1);
+ }
+
/* Release the instance lock to avoid deadlock with PJPROJECT group lock */
ao2_unlock(instance);
status = pj_turn_sock_create(&stun_config,
ast_sockaddr_is_ipv4(&addr) ? pj_AF_INET() : pj_AF_INET6(), conn_type,
- turn_cb, NULL, instance, turn_sock);
+ turn_cb, &turn_sock_cfg, instance, turn_sock);
+ ao2_cleanup(ice);
if (status != PJ_SUCCESS) {
ast_log(LOG_WARNING, "Could not create a TURN client socket\n");
ao2_lock(instance);
@@ -2532,6 +2543,17 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
return -1;
}
if (!rtp->passthrough) {
+ /* If a unidirectional ICE negotiation occurs then lock on to the source of the
+ * ICE traffic and use it as the target. This will occur if the remote side only
+ * wants to receive media but never send to us.
+ */
+ if (!rtp->ice_active_remote_candidates && !rtp->ice_proposed_remote_candidates) {
+ if (rtcp) {
+ ast_sockaddr_copy(&rtp->rtcp->them, sa);
+ } else {
+ ast_rtp_instance_set_remote_address(instance, sa);
+ }
+ }
return 0;
}
rtp->passthrough = 0;