diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_musiconhold.c | 156 | ||||
-rw-r--r-- | res/res_odbc.c | 70 | ||||
-rw-r--r-- | res/res_pjsip.c | 22 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 40 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_distributor.c | 242 | ||||
-rw-r--r-- | res/res_pjsip_sdp_rtp.c | 19 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 38 |
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; |