diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_config_sqlite3.c | 16 | ||||
-rw-r--r-- | res/res_pjproject.c | 224 | ||||
-rw-r--r-- | res/res_pjsip.c | 55 | ||||
-rw-r--r-- | res/res_pjsip/config_transport.c | 216 | ||||
-rw-r--r-- | res/res_pjsip_config_wizard.c | 111 | ||||
-rw-r--r-- | res/res_pjsip_diversion.c | 99 | ||||
-rw-r--r-- | res/res_pjsip_dtmf_info.c | 12 | ||||
-rw-r--r-- | res/res_pjsip_messaging.c | 6 | ||||
-rw-r--r-- | res/res_pjsip_mwi.c | 8 | ||||
-rw-r--r-- | res/res_pjsip_outbound_publish.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_outbound_registration.c | 32 | ||||
-rw-r--r-- | res/res_pjsip_pubsub.c | 31 | ||||
-rw-r--r-- | res/res_pjsip_send_to_voicemail.c | 16 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 33 | ||||
-rw-r--r-- | res/res_pjsip_t38.c | 41 | ||||
-rw-r--r-- | res/res_sorcery_memory_cache.c | 6 |
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(®ister_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; } |