diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_agi.c | 40 | ||||
-rw-r--r-- | res/res_pjsip/location.c | 56 | ||||
-rw-r--r-- | res/res_pjsip_caller_id.c | 6 | ||||
-rw-r--r-- | res/res_pjsip_registrar.c | 3 | ||||
-rw-r--r-- | res/res_pjsip_transport_management.c | 36 | ||||
-rw-r--r-- | res/stasis/control.c | 12 | ||||
-rw-r--r-- | res/stasis/control.h | 18 | ||||
-rw-r--r-- | res/stasis/stasis_bridge.c | 61 |
8 files changed, 187 insertions, 45 deletions
diff --git a/res/res_agi.c b/res/res_agi.c index f6ce74960..e8249e202 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -3736,6 +3736,24 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch return AGI_RESULT_SUCCESS; } + +AST_LIST_HEAD_NOLOCK(deferred_frames, ast_frame); + +static void queue_deferred_frames(struct deferred_frames *deferred_frames, + struct ast_channel *chan) +{ + struct ast_frame *f; + + if (!AST_LIST_EMPTY(deferred_frames)) { + ast_channel_lock(chan); + while ((f = AST_LIST_REMOVE_HEAD(deferred_frames, frame_list))) { + ast_queue_frame_head(chan, f); + ast_frfree(f); + } + ast_channel_unlock(chan); + } +} + static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[]) { struct ast_channel *c; @@ -3754,6 +3772,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi const char *sighup_str; const char *exit_on_hangup_str; int exit_on_hangup; + struct deferred_frames deferred_frames; + + AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); ast_channel_lock(chan); sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP"); @@ -3815,8 +3836,20 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi /* Write, ignoring errors */ if (write(agi->audio, f->data.ptr, f->datalen) < 0) { } + ast_frfree(f); + } else if (ast_is_deferrable_frame(f)) { + struct ast_frame *dup_f; + + if ((dup_f = ast_frisolate(f))) { + AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list); + } + + if (dup_f != f) { + ast_frfree(f); + } + } else { + ast_frfree(f); } - ast_frfree(f); } } else if (outfd > -1) { size_t len = sizeof(buf); @@ -3864,6 +3897,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi buf[buflen - 1] = '\0'; } + queue_deferred_frames(&deferred_frames, chan); + if (agidebug) ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf); cmd_status = agi_handle_command(chan, agi, buf, dead); @@ -3885,6 +3920,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi } } } + + queue_deferred_frames(&deferred_frames, chan); + if (agi->speech) { ast_speech_destroy(agi->speech); } diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 0a82f3a08..db4f9ac09 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -412,38 +412,64 @@ static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, in int ast_sip_validate_uri_length(const char *contact_uri) { - pjsip_uri *uri; - pjsip_sip_uri *sip_uri; - pj_pool_t *pool; int max_length = pj_max_hostname - 1; + char *contact = ast_strdupa(contact_uri); + char *host; + char *at; + int theres_a_port = 0; if (strlen(contact_uri) > pjsip_max_url_size - 1) { return -1; } - if (!(pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "uri validation", 512, 512))) { - ast_log(LOG_ERROR, "Unable to allocate pool for uri validation\n"); + contact = ast_strip_quoted(contact, "<", ">"); + + if (!strncasecmp(contact, "sip:", 4)) { + host = contact + 4; + } else if (!strncasecmp(contact, "sips:", 5)) { + host = contact + 5; + } else { + /* Not a SIP URI */ return -1; } - if (!(uri = pjsip_parse_uri(pool, (char *)contact_uri, strlen(contact_uri), 0)) || - (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) { - pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); - return -1; + at = strchr(contact, '@'); + if (at) { + /* sip[s]:user@host */ + host = at + 1; + } + + if (host[0] == '[') { + /* Host is an IPv6 address. Just get up to the matching bracket */ + char *close_bracket; + + close_bracket = strchr(host, ']'); + if (!close_bracket) { + return -1; + } + close_bracket++; + if (*close_bracket == ':') { + theres_a_port = 1; + } + *close_bracket = '\0'; + } else { + /* uri parameters could contain ';' so trim them off first */ + host = strsep(&host, ";?"); + /* Host is FQDN or IPv4 address. Need to find closing delimiter */ + if (strchr(host, ':')) { + theres_a_port = 1; + host = strsep(&host, ":"); + } } - sip_uri = pjsip_uri_get_uri(uri); - if (sip_uri->port == 0) { + if (!theres_a_port) { max_length -= strlen("_sips.tcp."); } - if (sip_uri->host.slen > max_length) { - pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + if (strlen(host) > max_length) { return -1; } - pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); - return 0; } diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c index 1818105d7..283ec94e5 100644 --- a/res/res_pjsip_caller_id.c +++ b/res/res_pjsip_caller_id.c @@ -424,6 +424,12 @@ static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_fromt ast_escape_quoted(id->name.str, name_buf, name_buf_len); pj_strdup2(tdata->pool, &id_name_addr->display, name_buf); + } else { + /* + * We need to clear the remnants of the clone or it'll be left set. + * pj_strdup2 is safe to call with a NULL src and it resets both slen and ptr. + */ + pj_strdup2(tdata->pool, &id_name_addr->display, NULL); } pj_strdup2(tdata->pool, &id_uri->user, id->number.str); diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index a94babd88..8edd6ee43 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -576,7 +576,6 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co ao2_cleanup(contact_update); } else { /* We want to report the user agent that was actually in the removed contact */ - user_agent = ast_strdupa(contact->user_agent); ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", @@ -585,7 +584,7 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co "UserAgent: %s", contact_uri, aor_name, - user_agent); + contact->user_agent); } } diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c index 862ae7216..8ba8c2da2 100644 --- a/res/res_pjsip_transport_management.c +++ b/res/res_pjsip_transport_management.c @@ -24,6 +24,8 @@ #include "asterisk.h" +#include <signal.h> + #include <pjsip.h> #include <pjsip_ua.h> @@ -93,7 +95,7 @@ static void *keepalive_transport_thread(void *data) /* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying * callback for the transport manager. */ - while (1) { + while (keepalive_interval) { sleep(keepalive_interval); ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL); } @@ -101,11 +103,29 @@ static void *keepalive_transport_thread(void *data) return NULL; } +AST_THREADSTORAGE(desc_storage); + static int idle_sched_cb(const void *data) { struct monitored_transport *keepalive = (struct monitored_transport *) data; int sip_received = ast_atomic_fetchadd_int(&keepalive->sip_received, 0); + if (!pj_thread_is_registered()) { + pj_thread_t *thread; + pj_thread_desc *desc; + + desc = ast_threadstorage_get(&desc_storage, sizeof(pj_thread_desc)); + if (!desc) { + ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage.\n"); + ao2_ref(keepalive, -1); + return 0; + } + + pj_bzero(*desc, sizeof(*desc)); + + pj_thread_register("Transport Monitor", *desc, &thread); + } + if (!sip_received) { ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n", keepalive->transport->info, IDLE_TIMEOUT); @@ -329,7 +349,19 @@ static int load_module(void) static int unload_module(void) { - /* This will never get called */ + pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()); + + if (keepalive_interval) { + keepalive_interval = 0; + pthread_kill(keepalive_thread, SIGURG); + pthread_join(keepalive_thread, NULL); + } + + ast_sched_context_destroy(sched); + ao2_ref(transports, -1); + + ast_sip_unregister_service(&idle_monitor_module); + pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback); return 0; } diff --git a/res/stasis/control.c b/res/stasis/control.c index 3c5b75041..aa6866aee 100644 --- a/res/stasis/control.c +++ b/res/stasis/control.c @@ -903,11 +903,8 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason, ast_bridge_after_cb_reason_string(reason)); } -int control_add_channel_to_bridge( - struct stasis_app_control *control, - struct ast_channel *chan, void *data) +int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { - struct ast_bridge *bridge = data; int res; if (!control || !bridge) { @@ -960,7 +957,7 @@ int control_add_channel_to_bridge( res = ast_bridge_impart(bridge, chan, - NULL, /* swap channel */ + swap, NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { @@ -976,6 +973,11 @@ int control_add_channel_to_bridge( return 0; } +int control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_channel *chan, void *data) +{ + return control_swap_channel_in_bridge(control, data, chan, NULL); +} + int stasis_app_control_add_channel_to_bridge( struct stasis_app_control *control, struct ast_bridge *bridge) { diff --git a/res/stasis/control.h b/res/stasis/control.h index 1d37a494a..868a8091b 100644 --- a/res/stasis/control.h +++ b/res/stasis/control.h @@ -111,12 +111,20 @@ struct stasis_app *control_app(struct stasis_app_control *control); * \brief Command callback for adding a channel to a bridge * * \param control The control for chan - * \param channel The channel on which commands should be executed - * \param bridge Data to be passed to the callback + * \param chan The channel on which commands should be executed + * \param data Bridge to be passed to the callback + */ +int control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_channel *chan, void *data); + +/*! + * \brief Command for swapping a channel in a bridge + * + * \param control The control for chan + * \param chan The channel on which commands should be executed + * \param bridge Bridge to be passed to the callback + * \param swap Channel to swap with when joining the bridge */ -int control_add_channel_to_bridge( - struct stasis_app_control *control, - struct ast_channel *chan, void *obj); +int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap); /*! * \brief Stop playing silence to a channel right now. diff --git a/res/stasis/stasis_bridge.c b/res/stasis/stasis_bridge.c index 1f501cad1..aa21ec29c 100644 --- a/res/stasis/stasis_bridge.c +++ b/res/stasis/stasis_bridge.c @@ -76,24 +76,54 @@ static void bridge_stasis_run_cb(struct ast_channel *chan, void *data) pbx_exec(chan, app_stasis, app_name); } -static int add_channel_to_bridge( +struct defer_bridge_add_obj { + /*! Bridge to join (has ref) */ + struct ast_bridge *bridge; + /*! + * \brief Channel to swap with in the bridge. (has ref) + * + * \note NULL if not swapping with a channel. + */ + struct ast_channel *swap; +}; + +static void defer_bridge_add_dtor(void *obj) +{ + struct defer_bridge_add_obj *defer = obj; + + ao2_cleanup(defer->bridge); + ast_channel_cleanup(defer->swap); +} + +static int defer_bridge_add( struct stasis_app_control *control, struct ast_channel *chan, void *obj) { - struct ast_bridge *bridge = obj; - int res; + struct defer_bridge_add_obj *defer = obj; - res = control_add_channel_to_bridge(control, - chan, bridge); - return res; + return control_swap_channel_in_bridge(control, defer->bridge, chan, defer->swap); } static void bridge_stasis_queue_join_action(struct ast_bridge *self, - struct ast_bridge_channel *bridge_channel) + struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { + struct defer_bridge_add_obj *defer; + + defer = ao2_alloc_options(sizeof(*defer), defer_bridge_add_dtor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!defer) { + return; + } + ao2_ref(self, +1); + defer->bridge = self; + if (swap) { + ast_channel_ref(swap->chan); + defer->swap = swap->chan; + } + ast_channel_lock(bridge_channel->chan); - command_prestart_queue_command(bridge_channel->chan, add_channel_to_bridge, - ao2_bump(self), __ao2_cleanup); + command_prestart_queue_command(bridge_channel->chan, defer_bridge_add, + defer, __ao2_cleanup); ast_channel_unlock(bridge_channel->chan); } @@ -167,18 +197,19 @@ static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel if (!control && !stasis_app_channel_is_internal(bridge_channel->chan)) { /* channel not in Stasis(), get it there */ + ast_debug(1, "Bridge %s: pushing non-stasis %p(%s) setup to come back in under stasis\n", + self->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); + /* Attach after-bridge callback and pass ownership of swap_app to it */ if (ast_bridge_set_after_callback(bridge_channel->chan, bridge_stasis_run_cb, NULL, NULL)) { - ast_log(LOG_ERROR, "Failed to set after bridge callback\n"); + ast_log(LOG_ERROR, + "Failed to set after bridge callback for bridge %s non-stasis push of %s\n", + self->uniqueid, ast_channel_name(bridge_channel->chan)); return -1; } - bridge_stasis_queue_join_action(self, bridge_channel); - if (swap) { - /* nudge the swap channel out of the bridge */ - ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0); - } + bridge_stasis_queue_join_action(self, bridge_channel, swap); /* Return -1 so the push fails and the after-bridge callback gets called * This keeps the bridging framework from putting the channel into the bridge |