summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
Diffstat (limited to 'res')
-rw-r--r--res/res_config_sqlite3.c16
-rw-r--r--res/res_pjproject.c224
-rw-r--r--res/res_pjsip.c55
-rw-r--r--res/res_pjsip/config_transport.c216
-rw-r--r--res/res_pjsip_config_wizard.c111
-rw-r--r--res/res_pjsip_diversion.c99
-rw-r--r--res/res_pjsip_dtmf_info.c12
-rw-r--r--res/res_pjsip_messaging.c6
-rw-r--r--res/res_pjsip_mwi.c8
-rw-r--r--res/res_pjsip_outbound_publish.c2
-rw-r--r--res/res_pjsip_outbound_registration.c32
-rw-r--r--res/res_pjsip_pubsub.c31
-rw-r--r--res/res_pjsip_send_to_voicemail.c16
-rw-r--r--res/res_pjsip_session.c33
-rw-r--r--res/res_pjsip_t38.c41
-rw-r--r--res/res_sorcery_memory_cache.c6
16 files changed, 736 insertions, 172 deletions
diff --git a/res/res_config_sqlite3.c b/res/res_config_sqlite3.c
index 0b0a78cf4..a30612368 100644
--- a/res/res_config_sqlite3.c
+++ b/res/res_config_sqlite3.c
@@ -127,8 +127,14 @@ static inline const char *sqlite3_escape_string_helper(struct ast_threadstorage
* add two quotes, and convert NULL pointers to the word "NULL", but we
* don't allow those anyway. Just going to use %q for now. */
struct ast_str *buf = ast_str_thread_get(ts, maxlen);
- char *tmp = ast_str_buffer(buf);
char q = ts == &escape_value_buf ? '\'' : '"';
+ char *tmp;
+
+ if (ast_str_size(buf) < maxlen) {
+ /* realloc if buf is too small */
+ ast_str_make_space(&buf, maxlen);
+ }
+ tmp = ast_str_buffer(buf);
ast_str_reset(buf);
*tmp++ = q; /* Initial quote */
@@ -160,9 +166,15 @@ static const char *sqlite3_escape_column_op(const char *param)
{
size_t maxlen = strlen(param) * 2 + sizeof("\"\" =");
struct ast_str *buf = ast_str_thread_get(&escape_column_buf, maxlen);
- char *tmp = ast_str_buffer(buf);
+ char *tmp;
int space = 0;
+ if (ast_str_size(buf) < maxlen) {
+ /* realloc if buf is too small */
+ ast_str_make_space(&buf, maxlen);
+ }
+ tmp = ast_str_buffer(buf);
+
ast_str_reset(buf);
*tmp++ = '"';
while ((*tmp++ = *param++)) {
diff --git a/res/res_pjproject.c b/res/res_pjproject.c
index 9e08bf3e0..9ed3d57a1 100644
--- a/res/res_pjproject.c
+++ b/res/res_pjproject.c
@@ -37,6 +37,44 @@
<support_level>core</support_level>
***/
+/*** DOCUMENTATION
+ <configInfo name="res_pjproject" language="en_US">
+ <synopsis>pjproject common configuration</synopsis>
+ <configFile name="pjproject.conf">
+ <configObject name="log_mappings">
+ <synopsis>PJPROJECT to Asterisk Log Level Mapping</synopsis>
+ <description><para>Warnings and errors in the pjproject libraries are generally handled
+ by Asterisk. In many cases, Asterisk wouldn't even consider them to
+ be warnings or errors so the messages emitted by pjproject directly
+ are either superfluous or misleading. The 'log_mappings'
+ object allows mapping the pjproject levels to Asterisk levels, or nothing.
+ </para>
+ <note><para>The id of this object, as well as its type, must be
+ 'log_mappings' or it won't be found.</para></note>
+ </description>
+ <configOption name="type">
+ <synopsis>Must be of type 'log_mappings'.</synopsis>
+ </configOption>
+ <configOption name="asterisk_error" default="0,1">
+ <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_ERROR.</synopsis>
+ </configOption>
+ <configOption name="asterisk_warning" default="2">
+ <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_WARNING.</synopsis>
+ </configOption>
+ <configOption name="asterisk_notice" default="">
+ <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_NOTICE.</synopsis>
+ </configOption>
+ <configOption name="asterisk_debug" default="3,4,5">
+ <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_DEBUG.</synopsis>
+ </configOption>
+ <configOption name="asterisk_verbose" default="">
+ <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_VERBOSE.</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+ ***/
+
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
@@ -51,7 +89,9 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/cli.h"
#include "asterisk/res_pjproject.h"
#include "asterisk/vector.h"
+#include "asterisk/sorcery.h"
+static struct ast_sorcery *pjproject_sorcery;
static pj_log_func *log_cb_orig;
static unsigned decor_orig;
@@ -70,6 +110,66 @@ static struct pjproject_log_intercept_data pjproject_log_intercept = {
.fd = -1,
};
+struct log_mappings {
+ /*! Sorcery object details */
+ SORCERY_OBJECT(details);
+ /*! These are all comma-separated lists of pjproject log levels */
+ AST_DECLARE_STRING_FIELDS(
+ /*! pjproject log levels mapped to Asterisk ERROR */
+ AST_STRING_FIELD(asterisk_error);
+ /*! pjproject log levels mapped to Asterisk WARNING */
+ AST_STRING_FIELD(asterisk_warning);
+ /*! pjproject log levels mapped to Asterisk NOTICE */
+ AST_STRING_FIELD(asterisk_notice);
+ /*! pjproject log levels mapped to Asterisk VERBOSE */
+ AST_STRING_FIELD(asterisk_verbose);
+ /*! pjproject log levels mapped to Asterisk DEBUG */
+ AST_STRING_FIELD(asterisk_debug);
+ );
+};
+
+static struct log_mappings *default_log_mappings;
+
+static struct log_mappings *get_log_mappings(void)
+{
+ struct log_mappings *mappings;
+
+ mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
+ if (!mappings) {
+ return ao2_bump(default_log_mappings);
+ }
+
+ return mappings;
+}
+
+#define __LOG_SUPPRESS -1
+
+static int get_log_level(int pj_level)
+{
+ RAII_VAR(struct log_mappings *, mappings, get_log_mappings(), ao2_cleanup);
+ unsigned char l;
+
+ if (!mappings) {
+ return __LOG_ERROR;
+ }
+
+ l = '0' + fmin(pj_level, 9);
+
+ if (strchr(mappings->asterisk_error, l)) {
+ return __LOG_ERROR;
+ } else if (strchr(mappings->asterisk_warning, l)) {
+ return __LOG_WARNING;
+ } else if (strchr(mappings->asterisk_notice, l)) {
+ return __LOG_NOTICE;
+ } else if (strchr(mappings->asterisk_verbose, l)) {
+ return __LOG_VERBOSE;
+ } else if (strchr(mappings->asterisk_debug, l)) {
+ return __LOG_DEBUG;
+ }
+
+ return __LOG_SUPPRESS;
+}
+
static void log_forwarder(int level, const char *data, int len)
{
int ast_level;
@@ -89,25 +189,19 @@ static void log_forwarder(int level, const char *data, int len)
return;
}
- /* Lower number indicates higher importance */
- switch (level) {
- case 0: /* level zero indicates fatal error, according to docs */
- case 1: /* 1 seems to be used for errors */
- ast_level = __LOG_ERROR;
- break;
- case 2: /* 2 seems to be used for warnings and errors */
- ast_level = __LOG_WARNING;
- break;
- default:
- ast_level = __LOG_DEBUG;
+ ast_level = get_log_level(level);
+
+ if (ast_level == __LOG_SUPPRESS) {
+ return;
+ }
+ if (ast_level == __LOG_DEBUG) {
/* For levels 3 and up, obey the debug level for res_pjproject */
mod_level = ast_opt_dbg_module ?
ast_debug_get_by_module("res_pjproject") : 0;
if (option_debug < level && mod_level < level) {
return;
}
- break;
}
/* PJPROJECT uses indention to indicate function call depth. We'll prepend
@@ -201,14 +295,105 @@ static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, s
return CLI_SUCCESS;
}
+static void mapping_destroy(void *object)
+{
+ struct log_mappings *mappings = object;
+
+ ast_string_field_free_memory(mappings);
+}
+
+static void *mapping_alloc(const char *name)
+{
+ struct log_mappings *mappings = ast_sorcery_generic_alloc(sizeof(*mappings), mapping_destroy);
+ if (!mappings) {
+ return NULL;
+ }
+ ast_string_field_init(mappings, 128);
+
+ return mappings;
+}
+
+static char *handle_pjproject_show_log_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_variable *objset;
+ struct ast_variable *i;
+ struct log_mappings *mappings;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pjproject show log mappings";
+ e->usage =
+ "Usage: pjproject show log mappings\n"
+ " Show pjproject to Asterisk log mappings\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "PJPROJECT to Asterisk log mappings:\n");
+ ast_cli(a->fd, "Asterisk Level : PJPROJECT log levels\n");
+
+ mappings = get_log_mappings();
+ if (!mappings) {
+ ast_log(LOG_ERROR, "Unable to retrieve pjproject log_mappings\n");
+ return CLI_SUCCESS;
+ }
+
+ objset = ast_sorcery_objectset_create(pjproject_sorcery, mappings);
+ if (!objset) {
+ ao2_ref(mappings, -1);
+ return CLI_SUCCESS;
+ }
+
+ for (i = objset; i; i = i->next) {
+ ast_cli(a->fd, "%-16s : %s\n", i->name, i->value);
+ }
+ ast_variables_destroy(objset);
+
+ ao2_ref(mappings, -1);
+ return CLI_SUCCESS;
+}
+
static struct ast_cli_entry pjproject_cli[] = {
AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
+ AST_CLI_DEFINE(handle_pjproject_show_log_mappings, "Show pjproject to Asterisk log mappings"),
};
static int load_module(void)
{
ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
+ if (!(pjproject_sorcery = ast_sorcery_open())) {
+ ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_sorcery_apply_default(pjproject_sorcery, "log_mappings", "config", "pjproject.conf,criteria=type=log_mappings");
+ if (ast_sorcery_object_register(pjproject_sorcery, "log_mappings", mapping_alloc, NULL, NULL)) {
+ ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
+ ast_sorcery_unref(pjproject_sorcery);
+ pjproject_sorcery = NULL;
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_debug", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_debug));
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_error", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_error));
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_warning", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_warning));
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_notice", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_notice));
+ ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_verbose", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_verbose));
+
+ default_log_mappings = ast_sorcery_alloc(pjproject_sorcery, "log_mappings", "log_mappings");
+ if (!default_log_mappings) {
+ ast_log(LOG_ERROR, "Unable to allocate memory for pjproject log_mappings\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ ast_string_field_set(default_log_mappings, asterisk_error, "0,1");
+ ast_string_field_set(default_log_mappings, asterisk_warning, "2");
+ ast_string_field_set(default_log_mappings, asterisk_debug, "3,4,5");
+
+ ast_sorcery_load(pjproject_sorcery);
+
pj_init();
decor_orig = pj_log_get_decor();
@@ -247,12 +432,27 @@ static int unload_module(void)
pj_shutdown();
+ ao2_cleanup(default_log_mappings);
+ default_log_mappings = NULL;
+
+ ast_sorcery_unref(pjproject_sorcery);
+
return 0;
}
+static int reload_module(void)
+{
+ if (pjproject_sorcery) {
+ ast_sorcery_reload(pjproject_sorcery);
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
+ .reload = reload_module,
.load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
);
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index e35529253..713d94ec5 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1029,6 +1029,14 @@
Value is in milliseconds; default is 100 ms.</para>
</description>
</configOption>
+ <configOption name="allow_reload" default="no">
+ <synopsis>Allow this transport to be reloaded.</synopsis>
+ <description>
+ <para>Allow this transport to be reloaded when res_pjsip is reloaded.
+ This option defaults to "no" because reloading a transport may disrupt
+ in-progress calls.</para>
+ </description>
+ </configOption>
</configObject>
<configObject name="contact">
<synopsis>A way of creating an aliased name to a SIP URI</synopsis>
@@ -2479,22 +2487,14 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
return 0;
}
-static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector)
+int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector)
{
- RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup);
- const char *transport_name = endpoint->transport;
-
- if (ast_strlen_zero(transport_name)) {
- return 0;
- }
-
- transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name);
- transport_state = ast_sip_get_transport_state(transport_name);
- if (!transport || !transport_state) {
- ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' for endpoint '%s'\n",
- transport_name, ast_sorcery_object_get_id(endpoint));
+ transport_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport));
+ if (!transport_state) {
+ ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport state for '%s'\n",
+ ast_sorcery_object_get_id(transport));
return -1;
}
@@ -2517,6 +2517,35 @@ static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpo
return 0;
}
+int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector)
+{
+ RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
+
+ if (ast_strlen_zero(transport_name)) {
+ return 0;
+ }
+
+ transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name);
+ if (!transport) {
+ ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s'\n",
+ transport_name);
+ return -1;
+ }
+
+ return ast_sip_set_tpselector_from_transport(transport, selector);
+}
+
+static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector)
+{
+ const char *transport_name = endpoint->transport;
+
+ if (ast_strlen_zero(transport_name)) {
+ return 0;
+ }
+
+ return ast_sip_set_tpselector_from_transport_name(endpoint->transport, selector);
+}
+
void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri)
{
pjsip_sip_uri *sip_uri;
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 0fcd7d923..61a979c88 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -18,6 +18,7 @@
#include "asterisk.h"
+#include <math.h>
#include <pjsip.h>
#include <pjlib.h>
@@ -107,6 +108,56 @@ static int internal_state_cmp(void *obj, void *arg, int flags)
return CMP_MATCH;
}
+/*! \brief hashing function for state objects */
+static int transport_state_hash(const void *obj, const int flags)
+{
+ const struct ast_sip_transport_state *object;
+ const char *key;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ object = obj;
+ key = object->id;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+ return ast_str_hash(key);
+}
+
+/*! \brief comparator function for state objects */
+static int transport_state_cmp(void *obj, void *arg, int flags)
+{
+ const struct ast_sip_transport_state *object_left = obj;
+ const struct ast_sip_transport_state *object_right = arg;
+ const char *right_key = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->id;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(object_left->id, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /* Not supported by container. */
+ ast_assert(0);
+ return 0;
+ default:
+ cmp = 0;
+ break;
+ }
+ if (cmp) {
+ return 0;
+ }
+ return CMP_MATCH;
+}
+
static int sip_transport_to_ami(const struct ast_sip_transport *transport,
struct ast_str **buf)
{
@@ -347,6 +398,44 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
}
+static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
+{
+ if (a->type != b->type) {
+ return -1;
+ }
+
+ if (pj_sockaddr_cmp(&a->host, &b->host)) {
+ return -1;
+ }
+
+ if ((a->localnet || b->localnet)
+ && ((!a->localnet != !b->localnet)
+ || ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr)
+ || ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask)))
+ {
+ return -1;
+ }
+
+ if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
+ return -1;
+ }
+
+ if (a->tls.method != b->tls.method
+ || a->tls.ciphers_num != b->tls.ciphers_num
+ || a->tls.proto != b->tls.proto
+ || a->tls.verify_client != b->tls.verify_client
+ || a->tls.verify_server != b->tls.verify_server
+ || a->tls.require_client_cert != b->tls.require_client_cert) {
+ return -1;
+ }
+
+ if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) {
+ return -1;
+ }
+
+ return 0;
+}
+
static void states_cleanup(void *states)
{
if (states) {
@@ -364,6 +453,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
pj_status_t res = -1;
+ int i;
+#define BIND_TRIES 3
+#define BIND_DELAY_US 100000
if (!states) {
return -1;
@@ -376,32 +468,39 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
*/
ao2_wrlock(states);
+ temp_state = internal_state_alloc(transport);
+ if (!temp_state) {
+ ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
+ return -1;
+ }
+
perm_state = find_internal_state_by_transport(transport);
if (perm_state) {
ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
- if (changes) {
+ if (!changes && !has_state_changed(perm_state->state, temp_state->state)) {
+ /* In case someone is using the deprecated fields, reset them */
+ transport->state = perm_state->state;
+ copy_state_to_transport(transport);
+ ao2_replace(perm_state->transport, transport);
+ return 0;
+ }
+
+ if (!transport->allow_reload) {
if (!perm_state->change_detected) {
perm_state->change_detected = 1;
ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
}
+ /* In case someone is using the deprecated fields, reset them */
+ transport->state = perm_state->state;
+ copy_state_to_transport(transport);
+ ao2_replace(perm_state->transport, transport);
+ return 0;
}
-
- /* In case someone is using the deprecated fields, reset them */
- transport->state = perm_state->state;
- copy_state_to_transport(transport);
- ao2_replace(perm_state->transport, transport);
- return 0;
- }
-
- temp_state = internal_state_alloc(transport);
- if (!temp_state) {
- ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
- goto error;
}
if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
- goto error;
+ return -1;
}
/* Set default port if not present */
@@ -418,26 +517,38 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
} else {
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
transport_id);
- goto error;
+ return -1;
}
if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
- goto error;
+ return -1;
}
}
if (transport->type == AST_TRANSPORT_UDP) {
- if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
- res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport);
- } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
- res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport);
+
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->transport) {
+ pjsip_udp_transport_pause(perm_state->state->transport,
+ PJSIP_UDP_TRANSPORT_DESTROY_SOCKET);
+ usleep(BIND_DELAY_US);
+ }
+
+ if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+ res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
+ &temp_state->state->host.ipv4, NULL, transport->async_operations,
+ &temp_state->state->transport);
+ } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
+ res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(),
+ &temp_state->state->host.ipv6, NULL, transport->async_operations,
+ &temp_state->state->transport);
+ }
}
if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
pj_sock_t sock;
pj_qos_params qos_params;
-
sock = pjsip_udp_transport_get_socket(temp_state->state->transport);
pj_sock_get_qos_params(sock, &qos_params);
set_qos(transport, &qos_params);
@@ -451,18 +562,37 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
cfg.async_cnt = transport->async_operations;
set_qos(transport, &cfg.qos_params);
- res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory);
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->factory
+ && perm_state->state->factory->destroy) {
+ perm_state->state->factory->destroy(perm_state->state->factory);
+ usleep(BIND_DELAY_US);
+ }
+
+ res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
+ &temp_state->state->factory);
+ }
} else if (transport->type == AST_TRANSPORT_TLS) {
if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
ast_sorcery_object_get_id(obj));
- goto error;
+ return -1;
}
temp_state->state->tls.password = pj_str((char*)transport->password);
set_qos(transport, &temp_state->state->tls.qos_params);
- res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory);
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->factory
+ && perm_state->state->factory->destroy) {
+ perm_state->state->factory->destroy(perm_state->state->factory);
+ usleep(BIND_DELAY_US);
+ }
+
+ res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
+ &temp_state->state->host, NULL, transport->async_operations,
+ &temp_state->state->factory);
+ }
} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
if (transport->cos || transport->tos) {
ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
@@ -475,17 +605,16 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
pj_strerror(res, msg, sizeof(msg));
ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
- goto error;
+ return -1;
}
copy_state_to_transport(transport);
- ao2_link(states, temp_state);
+ if (perm_state) {
+ ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
+ }
+ ao2_link_flags(states, temp_state, OBJ_NOLOCK);
return 0;
-
-error:
- ao2_unlink(states, temp_state);
- return -1;
}
/*! \brief Custom handler for type just makes sure the state is created */
@@ -577,6 +706,11 @@ static int privkey_file_to_str(const void *obj, const intptr_t *args, char **buf
static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct ast_sip_transport *transport = obj;
+ RAII_VAR(struct ast_sip_transport_state *, state, find_or_create_temporary_state(transport), ao2_cleanup);
+
+ if (!state) {
+ return -1;
+ }
if (!strcasecmp(var->value, "udp")) {
transport->type = AST_TRANSPORT_UDP;
@@ -592,6 +726,8 @@ static int transport_protocol_handler(const struct aco_option *opt, struct ast_v
return -1;
}
+ state->type = transport->type;
+
return 0;
}
@@ -1160,9 +1296,26 @@ struct ast_sip_transport_state *ast_sip_get_transport_state(const char *transpor
return state->state;
}
+static int populate_transport_states(void *obj, void *arg, int flags)
+{
+ struct internal_state *state = obj;
+ struct ao2_container *container = arg;
+
+ ao2_link(container, state->state);
+
+ return CMP_MATCH;
+}
+
struct ao2_container *ast_sip_get_transport_states(void)
{
- return ao2_container_clone(transport_states, 0);
+ struct ao2_container *states = ao2_container_alloc(DEFAULT_STATE_BUCKETS, transport_state_hash, transport_state_cmp);
+
+ if (!states) {
+ return NULL;
+ }
+
+ ao2_callback(transport_states, OBJ_NODATA | OBJ_MULTIPLE, populate_transport_states, states);
+ return states;
}
/*! \brief Initialize sorcery with transport support */
@@ -1209,6 +1362,7 @@ int ast_sip_initialize_sorcery_transport(void)
ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
+ ast_sorcery_object_field_register(sorcery, "transport", "allow_reload", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, allow_reload));
internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
diff --git a/res/res_pjsip_config_wizard.c b/res/res_pjsip_config_wizard.c
index 9d85a4615..cf09a5415 100644
--- a/res/res_pjsip_config_wizard.c
+++ b/res/res_pjsip_config_wizard.c
@@ -45,6 +45,7 @@ ASTERISK_REGISTER_FILE()
#include <pjsip.h>
#include "asterisk/astobj2.h"
+#include "asterisk/cli.h"
#include "asterisk/res_pjsip.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
@@ -276,7 +277,7 @@ struct object_type_wizard {
struct ast_config *last_config;
char object_type[];
};
-static AST_VECTOR(object_type_wizards, struct object_type_wizard *) object_type_wizards;
+static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_type_wizards;
/*! \brief Callbacks for vector deletes */
#define NOT_EQUALS(a, b) (a != b)
@@ -304,12 +305,15 @@ static struct object_type_wizard *find_wizard(const char *object_type)
{
int idx;
+ AST_VECTOR_RW_RDLOCK(&object_type_wizards);
for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
if (!strcmp(otw->object_type, object_type)) {
+ AST_VECTOR_RW_UNLOCK(&object_type_wizards);
return otw;
}
}
+ AST_VECTOR_RW_UNLOCK(&object_type_wizards);
return NULL;
}
@@ -1137,7 +1141,9 @@ static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery
otw->wizard_data = wizard_data;
otw->last_config = NULL;
strcpy(otw->object_type, object_type); /* Safe */
+ AST_VECTOR_RW_WRLOCK(&object_type_wizards);
AST_VECTOR_APPEND(&object_type_wizards, otw);
+ AST_VECTOR_RW_UNLOCK(&object_type_wizards);
ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
}
}
@@ -1177,19 +1183,118 @@ static void instance_destroying_observer(const char *name, struct ast_sorcery *s
ast_module_unref(ast_module_info->self);
}
+static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_sorcery *sorcery;
+ int idx;
+ FILE *f = NULL;
+ const char *fn = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pjsip export config_wizard primitives [to]";
+ e->usage =
+ "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
+ " Export the config_wizard objects as pjsip primitives to\n"
+ " the console or to <filename>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 5) {
+ char date[256]="";
+ time_t t;
+ fn = a->argv[5];
+
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+ f = fopen(fn, "w");
+ if (!f) {
+ ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
+ return CLI_FAILURE;
+ }
+
+ fprintf(f, ";!\n");
+ fprintf(f, ";! Automatically generated configuration file\n");
+ fprintf(f, ";! Filename: %s\n", fn);
+ fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
+ fprintf(f, ";! Creation Date: %s", date);
+ fprintf(f, ";!\n");
+ }
+
+ sorcery = ast_sip_get_sorcery();
+
+ AST_VECTOR_RW_RDLOCK(&object_type_wizards);
+ for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
+ struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
+ struct ao2_container *container;
+ struct ao2_iterator i;
+ void *o;
+
+ container = ast_sorcery_retrieve_by_fields(sorcery, otw->object_type, AST_RETRIEVE_FLAG_MULTIPLE, NULL);
+ if (!container) {
+ continue;
+ }
+
+ i = ao2_iterator_init(container, 0);
+ while ((o = ao2_iterator_next(&i))) {
+ struct ast_variable *vars;
+ struct ast_variable *v;
+
+ vars = ast_sorcery_objectset_create(sorcery, o);
+ if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
+ if (f) {
+ fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
+ } else {
+ ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
+ }
+ for (v = vars; v; v = v->next) {
+ if (!ast_strlen_zero(v->value)) {
+ if (f) {
+ fprintf(f, "%s = %s\n", v->name, v->value);
+ } else {
+ ast_cli(a->fd, "%s = %s\n", v->name, v->value);
+ }
+ }
+ }
+ }
+ ast_variables_destroy(vars);
+ ao2_ref(o, -1);
+ }
+ ao2_iterator_destroy(&i);
+ ao2_cleanup(container);
+ }
+ AST_VECTOR_RW_UNLOCK(&object_type_wizards);
+
+ if (f) {
+ fclose(f);
+ ast_cli(a->fd, "Wrote configuration to %s\n", fn);
+ }
+
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry config_wizard_cli[] = {
+ AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
+};
+
static int load_module(void)
{
- AST_VECTOR_INIT(&object_type_wizards, 12);
+ AST_VECTOR_RW_INIT(&object_type_wizards, 12);
ast_sorcery_global_observer_add(&global_observer);
+ ast_cli_register_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
+ ast_cli_unregister_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
ast_sorcery_global_observer_remove(&global_observer);
AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
- AST_VECTOR_FREE(&object_type_wizards);
+ AST_VECTOR_RW_FREE(&object_type_wizards);
return 0;
}
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index ea2c7cd13..f1a6ddf77 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -37,6 +37,39 @@
static const pj_str_t diversion_name = { "Diversion", 9 };
+/*!
+ * \internal
+ * \brief Determine if the given string is a SIP token.
+ * \since 13.8.0
+ *
+ * \param str String to determine if is a SIP token.
+ *
+ * \note A token is defined by RFC3261 Section 25.1
+ *
+ * \return Non-zero if the string is a SIP token.
+ */
+static int sip_is_token(const char *str)
+{
+ int is_token;
+
+ if (ast_strlen_zero(str)) {
+ /* An empty string is not a token. */
+ return 0;
+ }
+
+ is_token = 1;
+ do {
+ if (!isalnum(*str)
+ && !strchr("-.!%*_+`'~", *str)) {
+ /* The character is not allowed in a token. */
+ is_token = 0;
+ break;
+ }
+ } while (*++str);
+
+ return is_token;
+}
+
/*! \brief Diversion header reasons
*
* The core defines a bunch of constants used to define
@@ -46,7 +79,7 @@ static const pj_str_t diversion_name = { "Diversion", 9 };
*/
static const struct reasons {
enum AST_REDIRECTING_REASON code;
- char *const text;
+ const char *text;
} reason_table[] = {
{ AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
{ AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
@@ -59,39 +92,28 @@ static const struct reasons {
{ AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
{ AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
{ AST_REDIRECTING_REASON_AWAY, "away" },
- { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
- { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
+ { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" }, /* Non-standard */
+ { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" }, /* Non-standard */
};
static const char *reason_code_to_str(const struct ast_party_redirecting_reason *reason)
{
- int code = reason->code;
+ int idx;
+ int code;
/* use specific string if given */
if (!ast_strlen_zero(reason->str)) {
return reason->str;
}
- if (code >= 0 && code < ARRAY_LEN(reason_table)) {
- return reason_table[code].text;
- }
-
- return "unknown";
-}
-
-static enum AST_REDIRECTING_REASON reason_str_to_code(const char *text)
-{
- enum AST_REDIRECTING_REASON code = AST_REDIRECTING_REASON_UNKNOWN;
- int i;
-
- for (i = 0; i < ARRAY_LEN(reason_table); ++i) {
- if (!strcasecmp(text, reason_table[i].text)) {
- code = reason_table[i].code;
- break;
+ code = reason->code;
+ for (idx = 0; idx < ARRAY_LEN(reason_table); ++idx) {
+ if (code == reason_table[idx].code) {
+ return reason_table[idx].text;
}
}
- return code;
+ return "unknown";
}
static pjsip_fromto_hdr *get_diversion_header(pjsip_rx_data *rdata)
@@ -159,13 +181,31 @@ static void set_redirecting_reason(pjsip_fromto_hdr *hdr,
{
static const pj_str_t reason_name = { "reason", 6 };
pjsip_param *reason = pjsip_param_find(&hdr->other_param, &reason_name);
+ char *reason_str;
if (!reason) {
return;
}
set_redirecting_value(&data->str, &reason->value);
- data->code = reason_str_to_code(data->str);
+ if (!data->str) {
+ /* Oops, allocation failure */
+ return;
+ }
+ reason_str = ast_strdupa(data->str);
+
+ /* Remove any enclosing double-quotes */
+ if (*reason_str == '"') {
+ reason_str = ast_strip_quoted(reason_str, "\"", "\"");
+ }
+
+ data->code = ast_redirecting_reason_parse(reason_str);
+ if (data->code < 0) {
+ data->code = AST_REDIRECTING_REASON_UNKNOWN;
+ } else {
+ ast_free(data->str);
+ data->str = ast_strdup("");
+ }
}
static void set_redirecting(struct ast_sip_session *session,
@@ -251,6 +291,9 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
pjsip_sip_uri *uri;
pjsip_param *param;
pjsip_fromto_hdr *old_hdr;
+ const char *reason_str;
+ const char *quote_str;
+ char *reason_buf;
struct ast_party_id *id = &data->from;
pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;
@@ -272,7 +315,17 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
param->name = pj_str("reason");
- param->value = pj_str((char*)reason_code_to_str(&data->reason));
+
+ reason_str = reason_code_to_str(&data->reason);
+
+ /* Reason is either already quoted or it is a token to not need quotes added. */
+ quote_str = *reason_str == '\"' || sip_is_token(reason_str) ? "" : "\"";
+
+ reason_buf = pj_pool_alloc(tdata->pool, strlen(reason_str) + 3);
+ sprintf(reason_buf, "%s%s%s", quote_str, reason_str, quote_str);/* Safe */
+
+ param->value = pj_str(reason_buf);
+
pj_list_insert_before(&hdr->other_param, param);
hdr->uri = (pjsip_uri *) name_addr;
diff --git a/res/res_pjsip_dtmf_info.c b/res/res_pjsip_dtmf_info.c
index 78d529c30..47ccd1ae5 100644
--- a/res/res_pjsip_dtmf_info.c
+++ b/res/res_pjsip_dtmf_info.c
@@ -82,14 +82,13 @@ static char get_event(const char *c)
static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
{
pjsip_msg_body *body = rdata->msg_info.msg->body;
- char buf[body ? body->len : 0];
+ char buf[body ? body->len + 1 : 1];
char *cur = buf;
char *line;
-
char event = '\0';
unsigned int duration = 100;
-
char is_dtmf;
+ int res;
if (!session->channel) {
return 0;
@@ -107,7 +106,12 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj
return 0;
}
- body->print_body(body, buf, body->len);
+ res = body->print_body(body, buf, body->len);
+ if (res < 0) {
+ send_response(session, rdata, 500);
+ return 0;
+ }
+ buf[res] = '\0';
if (is_dtmf) {
/* directly use what is in the message body */
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 7532e39be..f72f3f015 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -530,6 +530,10 @@ static struct msg_data* msg_data_create(const struct ast_msg *msg, const char *t
/* Make sure we start with sip: */
mdata->to = ast_begins_with(to, "sip:") ? ast_strdup(++to) : ast_strdup(to - 3);
mdata->from = ast_strdup(from);
+ if (!mdata->to || !mdata->from) {
+ ao2_ref(mdata, -1);
+ return NULL;
+ }
/* sometimes from can still contain the tag at this point, so remove it */
if ((tag = strchr(mdata->from, ';'))) {
@@ -597,7 +601,7 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
if (!(mdata = msg_data_create(msg, to, from)) ||
ast_sip_push_task(message_serializer, msg_send, mdata)) {
- ao2_ref(mdata, -1);
+ ao2_cleanup(mdata);
return -1;
}
return 0;
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index f6600dd63..e1eea6f2a 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -448,7 +448,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
contacts = ast_sip_location_retrieve_aor_contacts(aor);
if (!contacts || (ao2_container_count(contacts) == 0)) {
- ast_log(LOG_NOTICE, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
+ ast_debug(1, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
continue;
}
@@ -600,7 +600,7 @@ static int mwi_validate_for_aor(void *obj, void *arg, int flags)
mailboxes = ast_strdupa(aor->mailboxes);
while ((mailbox = strsep(&mailboxes, ","))) {
if (endpoint_receives_unsolicited_mwi_for_mailbox(endpoint, mailbox)) {
- ast_log(LOG_NOTICE, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
+ ast_debug(1, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
"Denying MWI subscription to %s\n", ast_sorcery_object_get_id(endpoint), mailbox,
ast_sorcery_object_get_id(aor));
return -1;
@@ -710,13 +710,13 @@ static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
aor = ast_sip_location_retrieve_aor(resource);
if (!aor) {
- ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n",
+ ast_debug(1, "Unable to locate aor %s. MWI subscription failed.\n",
resource);
return 404;
}
if (ast_strlen_zero(aor->mailboxes)) {
- ast_log(LOG_NOTICE, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
+ ast_debug(1, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
resource);
return 404;
}
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index a58bcbb91..856d84a7a 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -893,7 +893,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
pjsip_publishc_destroy(client->client);
client->client = NULL;
- if (sip_outbound_publish_client_alloc(publish)) {
+ if (sip_outbound_publish_client_alloc(client)) {
ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
ast_sorcery_object_get_id(publish));
goto end;
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 0ff609a63..dd69ff20e 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -346,6 +346,8 @@ struct sip_outbound_registration_client_state {
unsigned int destroy:1;
/*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
unsigned int auth_attempted:1;
+ /*! \brief The name of the transport to be used for the registration */
+ char *transport_name;
};
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -508,6 +510,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
{
pj_status_t status;
int *callback_invoked;
+ pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
if (!callback_invoked) {
@@ -517,6 +520,13 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
/* Due to the message going out the callback may now be invoked, so bump the count */
ao2_ref(client_state, +1);
+ /*
+ * Set the transport in case transports were reloaded.
+ * When pjproject removes the extraneous error messages produced,
+ * we can check status and only set the transport and resend if there was an error
+ */
+ ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);
+ pjsip_regc_set_transport(client_state->client, &selector);
status = pjsip_regc_send(client_state->client, tdata);
/* If the attempt to send the message failed and the callback was not invoked we need to
@@ -966,6 +976,7 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
{
struct sip_outbound_registration_client_state *client_state = obj;
+ ast_free(client_state->transport_name);
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
sip_outbound_registration_status_str(client_state->status));
@@ -1003,6 +1014,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
state->client_state->timer.user_data = state->client_state;
state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+ state->client_state->transport_name = ast_strdup(registration->transport);
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
@@ -1171,25 +1183,6 @@ static int sip_outbound_registration_regc_alloc(void *data)
pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
- if (!ast_strlen_zero(registration->transport)) {
- RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(registration->transport), ao2_cleanup);
-
- if (!transport_state) {
- ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
- " for outbound registration", registration->transport);
- return -1;
- }
-
- if (transport_state->transport) {
- selector.type = PJSIP_TPSELECTOR_TRANSPORT;
- selector.u.transport = transport_state->transport;
- } else if (transport_state->factory) {
- selector.type = PJSIP_TPSELECTOR_LISTENER;
- selector.u.listener = transport_state->factory;
- } else {
- return -1;
- }
- }
ast_assert(state->client_state->client == NULL);
if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
@@ -1198,6 +1191,7 @@ static int sip_outbound_registration_regc_alloc(void *data)
return -1;
}
+ ast_sip_set_tpselector_from_transport_name(registration->transport, &selector);
pjsip_regc_set_transport(state->client_state->client, &selector);
if (!ast_strlen_zero(registration->outbound_proxy)) {
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index bde7075dd..0da43190c 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1559,6 +1559,28 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co
return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
}
+/*!
+ * \internal
+ * \brief Wrapper for pjsip_evsub_send_request
+ *
+ * This function (re)sets the transport before sending to catch cases
+ * where the transport might have changed.
+ *
+ * If pjproject gives us the ability to resend, we'll only reset the transport
+ * if PJSIP_ETPNOTAVAIL is returned from send.
+ *
+ * \returns pj_status_t
+ */
+static pj_status_t internal_pjsip_evsub_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
+{
+ pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+ ast_sip_set_tpselector_from_transport_name(sub_tree->endpoint->transport, &selector);
+ pjsip_dlg_set_transport(sub_tree->dlg, &selector);
+
+ return pjsip_evsub_send_request(sub_tree->evsub, tdata);
+}
+
/* XXX This function is not used. */
struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
struct ast_sip_endpoint *endpoint, const char *resource)
@@ -1606,7 +1628,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
evsub = sub_tree->evsub;
if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
- pjsip_evsub_send_request(evsub, tdata);
+ internal_pjsip_evsub_send_request(sub_tree, tdata);
} else {
/* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
* being called and terminating the subscription. Therefore, we don't
@@ -1687,8 +1709,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
{
#ifdef TEST_FRAMEWORK
struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
-#endif
pjsip_evsub *evsub = sub_tree->evsub;
+#endif
int res;
if (allocate_tdata_buffer(tdata)) {
@@ -1696,7 +1718,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
return -1;
}
- res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
+ res = internal_pjsip_evsub_send_request(sub_tree, tdata);
+
subscription_persistence_update(sub_tree, NULL);
ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
@@ -1705,7 +1728,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
pjsip_evsub_get_state_name(evsub),
ast_sorcery_object_get_id(endpoint));
- return res;
+ return (res == PJ_SUCCESS ? 0 : -1);
}
/*!
diff --git a/res/res_pjsip_send_to_voicemail.c b/res/res_pjsip_send_to_voicemail.c
index 9d2b5b14b..bd70bcfd7 100644
--- a/res/res_pjsip_send_to_voicemail.c
+++ b/res/res_pjsip_send_to_voicemail.c
@@ -47,7 +47,8 @@
#define SEND_TO_VM_HEADER_VALUE "feature_send_to_vm"
#define SEND_TO_VM_REDIRECT "REDIRECTING(reason)"
-#define SEND_TO_VM_REDIRECT_VALUE "\"send_to_vm\""
+#define SEND_TO_VM_REDIRECT_VALUE "send_to_vm"
+#define SEND_TO_VM_REDIRECT_QUOTED_VALUE "\"" SEND_TO_VM_REDIRECT_VALUE "\""
static void send_response(struct ast_sip_session *session, int code, struct pjsip_rx_data *rdata)
{
@@ -102,9 +103,13 @@ static int has_diversion_reason(pjsip_rx_data *rdata)
pjsip_param *reason;
pjsip_fromto_hdr *hdr = get_diversion_header(rdata);
- return hdr &&
- (reason = get_diversion_reason(hdr)) &&
- !pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_VALUE);
+ if (!hdr) {
+ return 0;
+ }
+ reason = get_diversion_reason(hdr);
+ return reason
+ && (!pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_QUOTED_VALUE)
+ || !pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_VALUE));
}
static int has_call_feature(pjsip_rx_data *rdata)
@@ -160,12 +165,10 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
sip_session_datastore->data = other_party;
if (ast_sip_session_add_datastore(session, sip_session_datastore)) {
- ast_channel_unref(other_party);
ao2_ref(sip_session_datastore, -1);
send_response(session, 500, rdata);
return -1;
}
- ao2_ref(sip_session_datastore, -1);
if (has_feature) {
pbx_builtin_setvar_helper(other_party, SEND_TO_VM_HEADER,
@@ -177,6 +180,7 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
SEND_TO_VM_REDIRECT_VALUE);
}
+ ao2_ref(sip_session_datastore, -1);
return 0;
}
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index e7dd5b91d..983687174 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -888,10 +888,32 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
return 0;
}
+/*!
+ * \internal
+ * \brief Wrapper for pjsip_inv_send_msg
+ *
+ * This function (re)sets the transport before sending to catch cases
+ * where the transport might have changed.
+ *
+ * If pjproject gives us the ability to resend, we'll only reset the transport
+ * if PJSIP_ETPNOTAVAIL is returned from send.
+ *
+ * \returns pj_status_t
+ */
+static pj_status_t internal_pjsip_inv_send_msg(pjsip_inv_session *inv, const char *transport_name, pjsip_tx_data *tdata)
+{
+ pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+ ast_sip_set_tpselector_from_transport_name(transport_name, &selector);
+ pjsip_dlg_set_transport(inv->dlg, &selector);
+
+ return pjsip_inv_send_msg(inv, tdata);
+}
+
void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
handle_outgoing_response(session, tdata);
- pjsip_inv_send_msg(session->inv_session, tdata);
+ internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
return;
}
@@ -1087,7 +1109,8 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip
}
handle_outgoing_request(session, tdata);
- pjsip_inv_send_msg(session->inv_session, tdata);
+ internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
+
return;
}
@@ -1852,7 +1875,7 @@ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct a
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) != PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
}
- pjsip_inv_send_msg(inv_session, tdata);
+ internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
return NULL;
}
return inv_session;
@@ -2005,7 +2028,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
} else {
- pjsip_inv_send_msg(inv_session, tdata);
+ internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
}
return;
}
@@ -2015,7 +2038,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
} else {
- pjsip_inv_send_msg(inv_session, tdata);
+ internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
}
ao2_cleanup(invite);
}
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 2bb6f03da..c02517104 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -570,41 +570,6 @@ static struct ast_sip_session_supplement t38_supplement = {
.outgoing_request = t38_outgoing_invite_request,
};
-static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
-{
- struct ast_datastore *datastore;
- struct ast_sip_session_media *session_media;
-
- if (!session->channel) {
- return 0;
- }
-
- datastore = ast_sip_session_get_datastore(session, "t38");
- if (!datastore) {
- return 0;
- }
-
- session_media = ao2_find(session->media, "image", OBJ_KEY);
- if (!session_media) {
- ao2_ref(datastore, -1);
- return 0;
- }
-
- t38_change_state(session, session_media, datastore->data, T38_REJECTED);
-
- ao2_ref(datastore, -1);
- ao2_ref(session_media, -1);
-
- return 0;
-}
-
-/*! \brief Supplement for handling a remote termination of T.38 state */
-static struct ast_sip_session_supplement t38_bye_supplement = {
- .method = "BYE",
- .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
- .incoming_request = t38_incoming_bye_request,
-};
-
/*! \brief Parse a T.38 image stream and store the attribute information */
static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_media *stream)
@@ -935,7 +900,6 @@ static int unload_module(void)
{
ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image");
ast_sip_session_unregister_supplement(&t38_supplement);
- ast_sip_session_unregister_supplement(&t38_bye_supplement);
return 0;
}
@@ -962,11 +926,6 @@ static int load_module(void)
goto end;
}
- if (ast_sip_session_register_supplement(&t38_bye_supplement)) {
- ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n");
- goto end;
- }
-
if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
goto end;
diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c
index 0421d8158..704372e12 100644
--- a/res/res_sorcery_memory_cache.c
+++ b/res/res_sorcery_memory_cache.c
@@ -1832,7 +1832,7 @@ static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struc
}
}
- if (a->argc > 6) {
+ if (a->argc < 5 || a->argc > 6) {
return CLI_SHOWUSAGE;
}
@@ -1886,7 +1886,7 @@ static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct
}
}
- if (a->argc > 6) {
+ if (a->argc < 5 || a->argc > 6) {
return CLI_SHOWUSAGE;
}
@@ -1945,7 +1945,7 @@ static char *sorcery_memory_cache_populate(struct ast_cli_entry *e, int cmd, str
}
}
- if (a->argc > 5) {
+ if (a->argc != 5) {
return CLI_SHOWUSAGE;
}