/*! \brief Allocate an \ref ast_ari_conf for config parsing */ static void *conf_alloc(void) { RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup); cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!cfg) { return NULL; } cfg->general = ao2_alloc_options(sizeof(*cfg->general), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!cfg->general) { return NULL; } aco_set_defaults(&general_option, "general", cfg->general); if (ast_string_field_init(cfg->general, 64)) { return NULL; } cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL); ao2_ref(cfg, +1); return cfg; }
/*! \brief Allocate an \ref ast_ari_conf for config parsing */ static void *conf_alloc(void) { struct ast_ari_conf *cfg; cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!cfg) { return NULL; } cfg->general = ao2_alloc_options(sizeof(*cfg->general), conf_general_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL); if (!cfg->users || !cfg->general || ast_string_field_init(cfg->general, 64) || aco_set_defaults(&general_option, "general", cfg->general)) { ao2_ref(cfg, -1); return NULL; } return cfg; }
static struct cache_entry *cache_entry_create(struct stasis_message_type *type, const char *id, struct stasis_message *snapshot) { RAII_VAR(struct cache_entry *, entry, NULL, ao2_cleanup); ast_assert(type != NULL); ast_assert(id != NULL); entry = ao2_alloc_options(sizeof(*entry), cache_entry_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!entry) { return NULL; } entry->id = ast_strdup(id); if (!entry->id) { return NULL; } ao2_ref(type, +1); entry->type = type; if (snapshot != NULL) { ao2_ref(snapshot, +1); entry->snapshot = snapshot; } ao2_ref(entry, +1); return entry; }
/*! \brief Observer callback for when a contact is created */ static void contact_expiration_observer_created(const void *object) { const struct ast_sip_contact *contact = object; struct contact_expiration *expiration; int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); if (ast_tvzero(contact->expiration_time)) { return; } expiration = ao2_alloc_options(sizeof(*expiration), contact_expiration_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!expiration) { return; } expiration->contact = (struct ast_sip_contact*)contact; ao2_ref(expiration->contact, +1); ao2_ref(expiration, +1); if ((expiration->sched = ast_sched_add(sched, expires, contact_expiration_expire, expiration)) < 0) { ao2_ref(expiration, -1); ast_log(LOG_ERROR, "Scheduled expiration for contact '%s' could not be performed, contact may persist past life\n", ast_sorcery_object_get_id(contact)); } else { ao2_link(contact_autoexpire, expiration); } ao2_ref(expiration, -1); }
int __ast_format_interface_register(const char *codec, const struct ast_format_interface *interface, struct ast_module *mod) { SCOPED_AO2WRLOCK(lock, interfaces); struct format_interface *format_interface; if (!interface->format_clone || !interface->format_destroy) { ast_log(LOG_ERROR, "Format interface for codec '%s' does not implement required callbacks\n", codec); return -1; } format_interface = ao2_find(interfaces, codec, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (format_interface) { ast_log(LOG_ERROR, "A format interface is already present for codec '%s'\n", codec); ao2_ref(format_interface, -1); return -1; } format_interface = ao2_alloc_options(sizeof(*format_interface) + strlen(codec) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!format_interface) { return -1; } format_interface->interface = interface; strcpy(format_interface->codec, codec); /* Safe */ /* Once registered a format interface cannot be unregistered. */ ast_module_shutdown_ref(mod); ao2_link_flags(interfaces, format_interface, OBJ_NOLOCK); ao2_ref(format_interface, -1); ast_verb(2, "Registered format interface for codec '%s'\n", codec); return 0; }
struct ast_named_lock *__ast_named_lock_get(const char *filename, int lineno, const char *func, enum ast_named_lock_type lock_type, const char *keyspace, const char *key) { struct ast_named_lock *lock = NULL; int concat_key_buff_len = strlen(keyspace) + strlen(key) + 2; char *concat_key = ast_alloca(concat_key_buff_len); sprintf(concat_key, "%s-%s", keyspace, key); /* Safe */ ao2_lock(named_locks); lock = ao2_find(named_locks, concat_key, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (lock) { ao2_unlock(named_locks); ast_assert((ao2_options_get(lock) & AO2_ALLOC_OPT_LOCK_MASK) == lock_type); return lock; } lock = ao2_alloc_options(sizeof(*lock) + concat_key_buff_len, NULL, lock_type); if (lock) { strcpy(lock->key, concat_key); /* Safe */ ao2_link_flags(named_locks, lock, OBJ_NOLOCK); } ao2_unlock(named_locks); return lock; }
static int bridge_builtin_set_limits(struct ast_bridge_features *features, struct ast_bridge_features_limits *limits, enum ast_bridge_hook_remove_flags remove_flags) { RAII_VAR(struct ast_bridge_features_limits *, feature_limits, NULL, ao2_cleanup); if (!limits->duration) { return -1; } /* Create limits hook_pvt data. */ ast_module_ref(ast_module_info->self); feature_limits = ao2_alloc_options(sizeof(*feature_limits), bridge_features_limits_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!feature_limits) { ast_module_unref(ast_module_info->self); return -1; } if (ast_bridge_features_limits_construct(feature_limits)) { return -1; } bridge_features_limits_copy(feature_limits, limits); feature_limits->quitting_time = ast_tvadd(ast_tvnow(), ast_samp2tv(feature_limits->duration, 1000)); /* Install limit hooks. */ ao2_ref(feature_limits, +1); if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA, feature_limits->duration, bridge_features_duration_callback, feature_limits, __ao2_cleanup, remove_flags)) { ast_log(LOG_ERROR, "Failed to schedule the duration limiter to the bridge channel.\n"); ao2_ref(feature_limits, -1); return -1; } if (!ast_strlen_zero(feature_limits->connect_sound)) { ao2_ref(feature_limits, +1); if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA, 1, bridge_features_connect_callback, feature_limits, __ao2_cleanup, remove_flags)) { ast_log(LOG_WARNING, "Failed to schedule connect sound to the bridge channel.\n"); ao2_ref(feature_limits, -1); } } if (feature_limits->warning && feature_limits->warning < feature_limits->duration) { ao2_ref(feature_limits, +1); if (ast_bridge_interval_hook(features, AST_BRIDGE_HOOK_TIMER_OPTION_MEDIA, feature_limits->duration - feature_limits->warning, bridge_features_warning_callback, feature_limits, __ao2_cleanup, remove_flags)) { ast_log(LOG_WARNING, "Failed to schedule warning sound playback to the bridge channel.\n"); ao2_ref(feature_limits, -1); } } return 0; }
/*! * \internal * \brief fopencookie()/funopen() stream allocation function. * * \retval stream_cookie on success. * \retval NULL on error. */ static struct ast_tcptls_stream *tcptls_stream_alloc(void) { struct ast_tcptls_stream *stream; stream = ao2_alloc_options(sizeof(*stream), tcptls_stream_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (stream) { stream->fd = -1; stream->timeout = -1; } return stream; }
static struct serializer *serializer_create(struct ast_threadpool *pool, struct ast_serializer_shutdown_group *shutdown_group) { struct serializer *ser; ser = ao2_alloc_options(sizeof(*ser), serializer_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!ser) { return NULL; } ao2_ref(pool, +1); ser->pool = pool; ser->shutdown_group = ao2_bump(shutdown_group); return ser; }
int ast_str_container_add(struct ao2_container *str_container, const char *add) { char *ao2_add; /* The ao2_add object is immutable so it doesn't need a lock of its own. */ ao2_add = ao2_alloc_options(strlen(add) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!ao2_add) { return -1; } strcpy(ao2_add, add);/* Safe */ ao2_link(str_container, ao2_add); ao2_ref(ao2_add, -1); return 0; }
static struct corosync_node *corosync_node_alloc(struct ast_event *event) { struct corosync_node *node; node = ao2_alloc_options(sizeof(*node), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!node) { return NULL; } memcpy(&node->eid, (struct ast_eid *)ast_event_get_ie_raw(event, AST_EVENT_IE_EID), sizeof(node->eid)); node->id = ast_event_get_ie_uint(event, AST_EVENT_IE_NODE_ID); ast_sockaddr_parse(&node->addr, ast_event_get_ie_str(event, AST_EVENT_IE_LOCAL_ADDR), PARSE_PORT_IGNORE); return node; }
int ast_sip_for_each_contact(const struct ast_sip_aor *aor, ao2_callback_fn on_contact, void *arg) { struct ao2_container *contacts; struct ao2_iterator i; int res = 0; void *object = NULL; if (!on_contact || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) { return 0; } i = ao2_iterator_init(contacts, 0); while ((object = ao2_iterator_next(&i))) { RAII_VAR(struct ast_sip_contact *, contact, object, ao2_cleanup); RAII_VAR(struct ast_sip_contact_wrapper *, wrapper, NULL, ao2_cleanup); const char *aor_id = ast_sorcery_object_get_id(aor); wrapper = ao2_alloc_options(sizeof(struct ast_sip_contact_wrapper), contact_wrapper_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!wrapper) { res = -1; break; } wrapper->contact_id = ast_malloc(strlen(aor_id) + strlen(contact->uri) + 2); if (!wrapper->contact_id) { res = -1; break; } sprintf(wrapper->contact_id, "%s/%s", aor_id, contact->uri); wrapper->aor_id = ast_strdup(aor_id); if (!wrapper->aor_id) { res = -1; break; } wrapper->contact = contact; ao2_bump(wrapper->contact); if ((res = on_contact(wrapper, arg, 0))) { break; } } ao2_iterator_destroy(&i); ao2_ref(contacts, -1); return res; }
struct stasis_topic_pool *stasis_topic_pool_create(struct stasis_topic *pooled_topic) { struct stasis_topic_pool *pool; pool = ao2_alloc_options(sizeof(*pool), topic_pool_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!pool) { return NULL; } pool->pool_container = ao2_container_alloc(TOPIC_POOL_BUCKETS, topic_pool_entry_hash, topic_pool_entry_cmp); if (!pool->pool_container) { ao2_cleanup(pool); return NULL; } ao2_ref(pooled_topic, +1); pool->pool_topic = pooled_topic; return pool; }
struct stasis_caching_topic *stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache) { RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup); struct stasis_subscription *sub; RAII_VAR(char *, new_name, NULL, ast_free); int ret; ret = ast_asprintf(&new_name, "%s-cached", stasis_topic_name(original_topic)); if (ret < 0) { return NULL; } caching_topic = ao2_alloc_options(sizeof(*caching_topic), stasis_caching_topic_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (caching_topic == NULL) { return NULL; } caching_topic->topic = stasis_topic_create(new_name); if (caching_topic->topic == NULL) { return NULL; } ao2_ref(cache, +1); caching_topic->cache = cache; sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0); if (sub == NULL) { return NULL; } ao2_ref(original_topic, +1); caching_topic->original_topic = original_topic; /* This is for the reference contained in the subscription above */ ao2_ref(caching_topic, +1); caching_topic->sub = sub; /* The subscription holds the reference, so no additional ref bump. */ return caching_topic; }
struct stasis_cache *stasis_cache_create(snapshot_get_id id_fn) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); cache = ao2_alloc_options(sizeof(*cache), cache_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!cache) { return NULL; } cache->entries = ao2_container_alloc(NUM_CACHE_BUCKETS, cache_entry_hash, cache_entry_cmp); if (!cache->entries) { return NULL; } cache->id_fn = id_fn; ao2_ref(cache, +1); return cache; }
/*! * \internal * \since 12.0.0 * \brief Allocate a new holding bridge wrapper with the given bridge name and bridge ID. * * \param bridge_name name of the bridge wrapper * \param bridge the bridge being wrapped * * \retval Pointer to the newly allocated holding bridge wrapper * \retval NULL if allocation failed. The bridge will be destroyed if this function fails. */ static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_name, struct ast_bridge *bridge) { struct wait_bridge_wrapper *bridge_wrapper; bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1, wait_bridge_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!bridge_wrapper) { ast_bridge_destroy(bridge, 0); return NULL; } strcpy(bridge_wrapper->name, bridge_name); bridge_wrapper->bridge = bridge; if (!ao2_link(wait_bridge_wrappers, bridge_wrapper)) { ao2_cleanup(bridge_wrapper); return NULL; } return bridge_wrapper; }
struct stasis_forward *stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic) { int res; size_t idx; RAII_VAR(struct stasis_forward *, forward, NULL, ao2_cleanup); if (!from_topic || !to_topic) { return NULL; } forward = ao2_alloc_options(sizeof(*forward), forward_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!forward) { return NULL; } /* Forwards to ourselves are implicit. */ if (to_topic == from_topic) { return ao2_bump(forward); } forward->from_topic = ao2_bump(from_topic); forward->to_topic = ao2_bump(to_topic); topic_lock_both(to_topic, from_topic); res = AST_VECTOR_APPEND(&to_topic->upstream_topics, from_topic); if (res != 0) { ao2_unlock(from_topic); ao2_unlock(to_topic); return NULL; } for (idx = 0; idx < AST_VECTOR_SIZE(&to_topic->subscribers); ++idx) { topic_add_subscription(from_topic, AST_VECTOR_GET(&to_topic->subscribers, idx)); } ao2_unlock(from_topic); ao2_unlock(to_topic); return ao2_bump(forward); }
static void bridge_stasis_queue_join_action(struct ast_bridge *self, 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, defer_bridge_add, defer, __ao2_cleanup); ast_channel_unlock(bridge_channel->chan); }
/*! \brief Allocate an \ref ast_ari_conf_user for config parsing */ static void *user_alloc(const char *cat) { RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); if (!cat) { return NULL; } ast_debug(3, "Allocating user %s\n", cat); user = ao2_alloc_options(sizeof(*user), user_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!user) { return NULL; } user->username = ast_strdup(cat); if (!user->username) { return NULL; } ao2_ref(user, +1); return user; }
static struct stasis_message *update_create(struct stasis_message *old_snapshot, struct stasis_message *new_snapshot) { RAII_VAR(struct stasis_cache_update *, update, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); ast_assert(old_snapshot != NULL || new_snapshot != NULL); update = ao2_alloc_options(sizeof(*update), stasis_cache_update_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!update) { return NULL; } if (old_snapshot) { ao2_ref(old_snapshot, +1); update->old_snapshot = old_snapshot; if (!new_snapshot) { ao2_ref(stasis_message_type(old_snapshot), +1); update->type = stasis_message_type(old_snapshot); } } if (new_snapshot) { ao2_ref(new_snapshot, +1); update->new_snapshot = new_snapshot; ao2_ref(stasis_message_type(new_snapshot), +1); update->type = stasis_message_type(new_snapshot); } msg = stasis_message_create(stasis_cache_update_type(), update); if (!msg) { return NULL; } ao2_ref(msg, +1); return msg; }
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint; struct unidentified_request *unid; int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; if (endpoint) { /* * ao2_find with OBJ_UNLINK always write locks the container before even searching * for the object. Since the majority case is that the object won't be found, do * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink * if needed. */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } apply_acls(rdata); return PJ_FALSE; } endpoint = ast_sip_identify_endpoint(rdata); if (endpoint) { unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } } if (!endpoint) { /* always use an artificial endpoint - per discussion no reason to have "alwaysauthreject" as an option. It is felt using it was a bug fix and it is not needed since we are not worried about breaking old stuff and we really don't want to enable the discovery of SIP accounts */ endpoint = ast_sip_get_artificial_endpoint(); } /* endpoint ref held by mod_data[] */ rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint; if (endpoint == artificial_endpoint && !is_ack) { char name[AST_UUID_STR_LEN] = ""; pjsip_uri *from = rdata->msg_info.from->uri; if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) { pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from); ast_copy_pj_str(name, &sip_from->user, sizeof(name)); } unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { check_endpoint(rdata, unid, name); ao2_ref(unid, -1); } else if (using_auth_username) { ao2_wrlock(unidentified_requests); /* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (unid) { check_endpoint(rdata, unid, name); } else { unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1, NULL, AO2_ALLOC_OPT_LOCK_RWLOCK); if (!unid) { ao2_unlock(unidentified_requests); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */ unid->first_seen = ast_tvnow(); unid->count = 1; ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK); } ao2_ref(unid, -1); ao2_unlock(unidentified_requests); } else { log_failed_request(rdata, "No matching endpoint found", 0, 0); ast_sip_report_invalid_endpoint(name, rdata); } } apply_acls(rdata); return PJ_FALSE; }
struct ast_channel *stasis_app_control_snoop(struct ast_channel *chan, enum stasis_app_snoop_direction spy, enum stasis_app_snoop_direction whisper, const char *app, const char *app_args, const char *snoop_id) { RAII_VAR(struct stasis_app_snoop *, snoop, NULL, ao2_cleanup); struct ast_format_cap *caps; pthread_t thread; struct ast_assigned_ids assignedids = { .uniqueid = snoop_id, }; if (spy == STASIS_SNOOP_DIRECTION_NONE && whisper == STASIS_SNOOP_DIRECTION_NONE) { return NULL; } snoop = ao2_alloc_options(sizeof(*snoop), snoop_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!snoop) { return NULL; } /* Allocate a buffer to store the Stasis application and arguments in */ snoop->app = ast_str_create(64); if (!snoop->app) { return NULL; } ast_str_set(&snoop->app, 0, "%s", app); if (!ast_strlen_zero(app_args)) { ast_str_append(&snoop->app, 0, ",%s", app_args); } /* Set up a timer for the Snoop channel so it wakes up at a specific interval */ snoop->timer = ast_timer_open(); if (!snoop->timer) { return NULL; } ast_timer_set_rate(snoop->timer, 1000 / SNOOP_INTERVAL); /* Determine which signed linear format should be used */ snoop_determine_format(chan, snoop); /* Allocate a Snoop channel and set up various parameters */ snoop->chan = ast_channel_alloc(1, AST_STATE_UP, "", "", "", "", "", &assignedids, NULL, 0, "Snoop/%s-%08x", ast_channel_uniqueid(chan), (unsigned)ast_atomic_fetchadd_int((int *)&chan_idx, +1)); if (!snoop->chan) { return NULL; } ast_copy_string(snoop->uniqueid, ast_channel_uniqueid(chan), sizeof(snoop->uniqueid)); /* To keep the channel valid on the Snoop structure until it is destroyed we bump the ref up here */ ast_channel_ref(snoop->chan); ast_channel_tech_set(snoop->chan, &snoop_tech); ao2_ref(snoop, +1); ast_channel_tech_pvt_set(snoop->chan, snoop); ast_channel_set_fd(snoop->chan, 0, ast_timer_fd(snoop->timer)); /* The format on the Snoop channel will be this signed linear format, and it will never change */ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!caps) { ast_channel_unlock(snoop->chan); ast_hangup(snoop->chan); return NULL; } ast_format_cap_append(caps, snoop->spy_format, 0); ast_channel_nativeformats_set(snoop->chan, caps); ao2_ref(caps, -1); ast_channel_set_writeformat(snoop->chan, snoop->spy_format); ast_channel_set_rawwriteformat(snoop->chan, snoop->spy_format); ast_channel_set_readformat(snoop->chan, snoop->spy_format); ast_channel_set_rawreadformat(snoop->chan, snoop->spy_format); ast_channel_unlock(snoop->chan); if (spy != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_SPY, spy, &snoop->spy_direction, &snoop->spy)) { ast_hangup(snoop->chan); return NULL; } snoop->spy_samples = ast_format_get_sample_rate(snoop->spy_format) / (1000 / SNOOP_INTERVAL); snoop->spy_active = 1; } /* If whispering is enabled set up the audiohook */ if (whisper != STASIS_SNOOP_DIRECTION_NONE) { if (snoop_setup_audiohook(chan, AST_AUDIOHOOK_TYPE_WHISPER, whisper, &snoop->whisper_direction, &snoop->whisper)) { ast_hangup(snoop->chan); return NULL; } snoop->whisper_active = 1; } /* Create the thread which services the Snoop channel */ ao2_ref(snoop, +1); if (ast_pthread_create_detached_background(&thread, NULL, snoop_stasis_thread, snoop)) { ao2_cleanup(snoop); /* No other thread is servicing this channel so we can immediately hang it up */ ast_hangup(snoop->chan); return NULL; } publish_chanspy_message(snoop, 1); /* The caller of this has a reference as well */ return ast_channel_ref(snoop->chan); }
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data) { RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); size_t size; int res = 0; ast_assert(name != NULL); ast_assert(handler != NULL); ast_verb(1, "Creating Stasis app '%s'\n", name); size = sizeof(*app) + strlen(name) + 1; app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX); if (!app) { return NULL; } app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT, forwards_sort, NULL); if (!app->forwards) { return NULL; } app->topic = stasis_topic_create(name); if (!app->topic) { return NULL; } app->bridge_router = stasis_message_router_create(ast_bridge_topic_all()); if (!app->bridge_router) { return NULL; } res |= stasis_message_router_add(app->bridge_router, ast_bridge_merge_message_type(), bridge_merge_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_blind_transfer_type(), bridge_blind_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_attended_transfer_type(), bridge_attended_transfer_handler, app); res |= stasis_message_router_set_default(app->bridge_router, bridge_default_handler, app); if (res != 0) { return NULL; } /* Bridge router holds a reference */ ao2_ref(app, +1); app->router = stasis_message_router_create(app->topic); if (!app->router) { return NULL; } res |= stasis_message_router_add_cache_update(app->router, ast_bridge_snapshot_type(), sub_bridge_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_channel_snapshot_type(), sub_channel_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app); res |= stasis_message_router_set_default(app->router, sub_default_handler, app); if (res != 0) { return NULL; } /* Router holds a reference */ ao2_ref(app, +1); strncpy(app->name, name, size - sizeof(*app)); app->handler = handler; ao2_ref(data, +1); app->data = data; ao2_ref(app, +1); return app; }
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model) { RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); size_t size; int res = 0; size_t context_size = strlen("stasis-") + strlen(name) + 1; char context_name[context_size]; ast_assert(name != NULL); ast_assert(handler != NULL); ast_verb(1, "Creating Stasis app '%s'\n", name); size = sizeof(*app) + strlen(name) + 1; app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX); if (!app) { return NULL; } app->subscription_model = subscription_model; app->forwards = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT, forwards_sort, NULL); if (!app->forwards) { return NULL; } app->topic = stasis_topic_create(name); if (!app->topic) { return NULL; } app->bridge_router = stasis_message_router_create(ast_bridge_topic_all()); if (!app->bridge_router) { return NULL; } res |= stasis_message_router_add(app->bridge_router, ast_bridge_merge_message_type(), bridge_merge_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_blind_transfer_type(), bridge_blind_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, ast_attended_transfer_type(), bridge_attended_transfer_handler, app); res |= stasis_message_router_add(app->bridge_router, stasis_subscription_change_type(), bridge_subscription_change_handler, app); if (res != 0) { return NULL; } /* Bridge router holds a reference */ ao2_ref(app, +1); app->router = stasis_message_router_create(app->topic); if (!app->router) { return NULL; } res |= stasis_message_router_add(app->router, ast_bridge_snapshot_type(), sub_bridge_update_handler, app); res |= stasis_message_router_add(app->router, ast_channel_snapshot_type(), sub_channel_update_handler, app); res |= stasis_message_router_add_cache_update(app->router, ast_endpoint_snapshot_type(), sub_endpoint_update_handler, app); res |= stasis_message_router_add(app->router, stasis_subscription_change_type(), sub_subscription_change_handler, app); stasis_message_router_set_formatters_default(app->router, sub_default_handler, app, STASIS_SUBSCRIPTION_FORMATTER_JSON); if (res != 0) { return NULL; } /* Router holds a reference */ ao2_ref(app, +1); strncpy(app->name, name, size - sizeof(*app)); app->handler = handler; app->data = ao2_bump(data); /* Create a context, a match-all extension, and a 'h' extension for this application. Note that * this should only be done if a context does not already exist. */ strcpy(context_name, "stasis-"); strcat(context_name, name); if (!ast_context_find(context_name)) { if (!ast_context_find_or_create(NULL, NULL, context_name, "res_stasis")) { ast_log(LOG_WARNING, "Could not create context '%s' for Stasis application '%s'\n", context_name, name); } else { ast_add_extension(context_name, 0, "_.", 1, NULL, NULL, "Stasis", ast_strdup(name), ast_free_ptr, "res_stasis"); ast_add_extension(context_name, 0, "h", 1, NULL, NULL, "NoOp", NULL, NULL, "res_stasis"); } } else { ast_log(LOG_WARNING, "Not creating context '%s' for Stasis application '%s' because it already exists\n", context_name, name); } ao2_ref(app, +1); return app; }
int ast_sip_initialize_distributor(void) { unidentified_requests = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, DEFAULT_SUSPECTS_BUCKETS, suspects_hash, NULL, suspects_compare); if (!unidentified_requests) { return -1; } dialog_associations = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, DIALOG_ASSOCIATIONS_BUCKETS, dialog_associations_hash, NULL, dialog_associations_cmp); if (!dialog_associations) { ast_sip_destroy_distributor(); return -1; } if (distributor_pool_setup()) { ast_sip_destroy_distributor(); return -1; } prune_context = ast_sched_context_create(); if (!prune_context) { ast_sip_destroy_distributor(); return -1; } if (ast_sched_start_thread(prune_context)) { ast_sip_destroy_distributor(); return -1; } ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer); ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); if (create_artificial_endpoint() || create_artificial_auth()) { ast_sip_destroy_distributor(); return -1; } if (ast_sip_register_service(&distributor_mod)) { ast_sip_destroy_distributor(); return -1; } if (ast_sip_register_service(&endpoint_mod)) { ast_sip_destroy_distributor(); return -1; } if (ast_sip_register_service(&auth_mod)) { ast_sip_destroy_distributor(); return -1; } unid_formatter = ao2_alloc_options(sizeof(struct ast_sip_cli_formatter_entry), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!unid_formatter) { ast_sip_destroy_distributor(); ast_log(LOG_ERROR, "Unable to allocate memory for unid_formatter\n"); return -1; } unid_formatter->name = "unidentified_request"; unid_formatter->print_header = cli_unid_print_header; unid_formatter->print_body = cli_unid_print_body; unid_formatter->get_container = cli_unid_get_container; unid_formatter->iterate = cli_unid_iterate; unid_formatter->get_id = cli_unid_get_id; unid_formatter->retrieve_by_id = cli_unid_retrieve_by_id; ast_sip_register_cli_formatter(unid_formatter); ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); return 0; }
static struct topic_pool_entry *topic_pool_entry_alloc(void) { return ao2_alloc_options(sizeof(struct topic_pool_entry), topic_pool_entry_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); }
static void register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts, struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0; int updated = 0; int deleted = 0; int permanent = 0; int contact_count; struct ao2_container *existing_contacts = NULL; pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* We create a single pool and use it throughout this function where we need one */ details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 1024, 256); if (!details.pool) { response->code = 500; return; } /* If there are any permanent contacts configured on the AOR we need to take them * into account when counting contacts. */ if (aor->permanent_contacts) { permanent = ao2_container_count(aor->permanent_contacts); } if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); response->code = 400; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); response->code = 420; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (aor->remove_existing) { /* Cumulative number of contacts affected by this registration */ contact_count = MAX(updated + added - deleted, 0); /* We need to keep track of only existing contacts so we can later * remove them if need be. */ existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, ast_sorcery_object_id_compare); if (!existing_contacts) { response->code = 500; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts); } else { /* Total contacts after this registration */ contact_count = ao2_container_count(contacts) - permanent + added - deleted; } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); response->code = 403; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); ao2_cleanup(existing_contacts); return; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); /* If we are keeping track of existing contacts for removal then, well, there is * absolutely nothing left so no need to try to remove any. */ if (existing_contacts) { ao2_ref(existing_contacts, -1); existing_contacts = NULL; } break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); /* If a contact was returned and we need to keep track of existing contacts then it * should be removed. */ if (contact && existing_contacts) { ao2_unlink(existing_contacts, contact); } if (!contact) { int prune_on_boot; /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata); contact = ast_sip_location_create_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint); if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } if (prune_on_boot) { const char *contact_name; struct contact_transport_monitor *monitor; /* * Monitor the transport in case it gets disconnected because * the contact won't be valid anymore if that happens. */ contact_name = ast_sorcery_object_get_id(contact); monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (monitor) { strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_register(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor); ao2_ref(monitor, -1); } } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); ao2_link(contacts, contact); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_link(contacts, contact_update); ao2_cleanup(contact_update); } else { if (contact->prune_on_boot) { struct contact_transport_monitor *monitor; const char *contact_name = ast_sorcery_object_get_id(contact); monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name)); strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_unregister(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher); } /* We want to report the user agent that was actually in the removed contact */ 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", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* * If the AOR is configured to remove any contacts over max_contacts * that have not been updated/added/deleted as a result of this * REGISTER do so. * * The existing contacts container holds all contacts that were not * involved in this REGISTER. * The contacts container holds the current contacts of the AOR. */ if (aor->remove_existing && existing_contacts) { /* Total contacts after this registration */ contact_count = ao2_container_count(existing_contacts) + updated + added; if (contact_count > aor->max_contacts) { /* Remove excess existing contacts that expire the soonest */ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts); } ao2_ref(existing_contacts, -1); } response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); response->code = 500; return; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } response->tdata = tdata; }