/* * This function is run in the context of the serializer. * It runs the task with a simple call and reschedules based on the result. */ static int run_task(void *data) { RAII_VAR(struct ast_sip_sched_task *, schtd, ao2_bump(data), ao2_cleanup); int res; int delay; ao2_lock(schtd); schtd->last_start = ast_tvnow(); schtd->is_running = 1; schtd->run_count++; ao2_unlock(schtd); res = schtd->task(schtd->task_data); ao2_lock(schtd); schtd->is_running = 0; schtd->last_end = ast_tvnow(); /* * Don't restart if the task returned 0 or if the interval * was set to 0 while the task was running */ if (!res || !schtd->interval) { schtd->interval = 0; ao2_unlock(schtd); ao2_unlink(tasks, schtd); return -1; } if (schtd->flags & AST_SIP_SCHED_TASK_VARIABLE) { schtd->interval = res; } if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) { delay = schtd->interval; } else { delay = schtd->interval - (ast_tvdiff_ms(schtd->last_end, schtd->last_start) % schtd->interval); } schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, (const void *)schtd); if (schtd->current_scheduler_id < 0) { schtd->interval = 0; ao2_unlock(schtd); ao2_unlink(tasks, schtd); return -1; } ao2_unlock(schtd); return 0; }
struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, int target) { RAII_VAR(struct parked_user *, user, NULL, ao2_cleanup); if (target < 0) { user = ao2_callback(lot->parked_users, 0, NULL, NULL); } else { user = ao2_callback(lot->parked_users, 0, retrieve_parked_user_targeted, &target); } if (!user) { return NULL; } ao2_lock(user); if (user->resolution != PARK_UNSET) { /* Abandon. Something else has resolved the parked user before we got to it. */ ao2_unlock(user); return NULL; } ao2_unlink(lot->parked_users, user); user->resolution = PARK_ANSWERED; ao2_unlock(user); parking_lot_remove_if_unused(user->lot); /* Bump the ref count by 1 since the RAII_VAR will eat the reference otherwise */ ao2_ref(user, +1); return user; }
static void pthread_timer_close(void *data) { struct pthread_timer *timer = data; ao2_unlink(pthread_timers, timer); ao2_ref(timer, -1); }
void ast_ari_websocket_events_event_websocket_established( struct ast_ari_websocket_session *ws_session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args) { RAII_VAR(struct event_session *, session, NULL, event_session_cleanup); struct ast_json *msg; const char *session_id; ast_debug(3, "/events WebSocket established\n"); ast_assert(ws_session != NULL); session_id = ast_ari_websocket_session_id(ws_session); /* Find the event_session and update its websocket */ session = ao2_find(event_session_registry, session_id, OBJ_SEARCH_KEY); if (session) { ao2_unlink(event_session_registry, session); event_session_update_websocket(session, ws_session); } else { ast_log(LOG_WARNING, "Failed to locate an event session for the provided websocket session\n"); } /* We don't process any input, but we'll consume it waiting for EOF */ while ((msg = ast_ari_websocket_session_read(ws_session))) { ast_json_unref(msg); } }
static void * del_element(unsigned int *seedp) { char keybuf[100]; struct ht_element *el, lookup; int x; /* pick a random element from 0 to highwater-1 */ x = my_rand(0,glob_highwater-1,seedp); sprintf(keybuf, "key%08d", x); #ifdef DEBUG printf("- %s", keybuf); #endif lookup.key = keybuf; el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); if (el) { #ifdef DEBUG printf("...YES (el=%x)\n", (unsigned long)el); #endif ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */ els_removed++; } else { #ifdef DEBUG printf("...NO.\n"); #endif return 0; } return el; }
/*! * \brief Add threads to the threadpool * * This function is called from the threadpool's control taskprocessor thread. * \param pool The pool that is expanding * \delta The number of threads to add to the pool */ static void grow(struct ast_threadpool *pool, int delta) { int i; int current_size = ao2_container_count(pool->active_threads) + ao2_container_count(pool->idle_threads); if (pool->options.max_size && current_size + delta > pool->options.max_size) { delta = pool->options.max_size - current_size; } ast_debug(3, "Increasing threadpool %s's size by %d\n", ast_taskprocessor_name(pool->tps), delta); for (i = 0; i < delta; ++i) { struct worker_thread *worker = worker_thread_alloc(pool); if (!worker) { return; } if (ao2_link(pool->idle_threads, worker)) { if (worker_thread_start(worker)) { ast_log(LOG_ERROR, "Unable to start worker thread %d. Destroying.\n", worker->id); ao2_unlink(pool->active_threads, worker); } } else { ast_log(LOG_WARNING, "Failed to activate worker thread %d. Destroying.\n", worker->id); } ao2_ref(worker, -1); } }
/*! \brief Hangup a call through the local proxy channel */ static int local_hangup(struct ast_channel *ast) { struct local_pvt *p = ast_channel_tech_pvt(ast); int res; if (!p) { return -1; } /* give the pvt a ref to fulfill calling requirements. */ ao2_ref(p, +1); res = ast_unreal_hangup(&p->base, ast); if (!res) { int unlink; ao2_lock(p); unlink = !p->base.owner && !p->base.chan; ao2_unlock(p); if (unlink) { ao2_unlink(locals, p); } } ao2_ref(p, -1); return res; }
/*! * \brief Activate idle threads * * This function always returns CMP_MATCH because all workers that this * function acts on need to be seen as matches so they are unlinked from the * list of idle threads. * * Called as an ao2_callback in the threadpool's control taskprocessor thread. * \param obj The worker to activate * \param arg The pool where the worker belongs * \retval CMP_MATCH */ static int activate_thread(void *obj, void *arg, int flags) { struct worker_thread *worker = obj; struct ast_threadpool *pool = arg; if (!ao2_link(pool->active_threads, worker)) { /* If we can't link the idle thread into the active container, then * we'll just leave the thread idle and not wake it up. */ ast_log(LOG_WARNING, "Failed to activate thread %d. Remaining idle\n", worker->id); return 0; } if (worker_set_state(worker, ALIVE)) { ast_debug(1, "Failed to activate thread %d. It is dead\n", worker->id); /* The worker thread will no longer exist in the active threads or * idle threads container after this. */ ao2_unlink(pool->active_threads, worker); } return CMP_MATCH; }
struct ast_channel *ast_pickup_find_by_group(struct ast_channel *chan) { struct ao2_container *candidates;/*!< Candidate channels found to pickup. */ struct ast_channel *target;/*!< Potential pickup target */ candidates = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL); if (!candidates) { return NULL; } /* Find all candidate targets by group. */ ast_channel_callback(find_channel_by_group, chan, candidates, 0); /* Find the oldest pickup target candidate */ target = NULL; for (;;) { struct ast_channel *candidate;/*!< Potential new older target */ struct ao2_iterator iter; iter = ao2_iterator_init(candidates, 0); while ((candidate = ao2_iterator_next(&iter))) { if (!target) { /* First target. */ target = candidate; continue; } if (ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) { /* We have a new target. */ ast_channel_unref(target); target = candidate; continue; } ast_channel_unref(candidate); } ao2_iterator_destroy(&iter); if (!target) { /* No candidates found. */ break; } /* The found channel must be locked and ref'd. */ ast_channel_lock(target); /* Recheck pickup ability */ if (ast_can_pickup(target)) { /* This is the channel to pickup. */ break; } /* Someone else picked it up or the call went away. */ ast_channel_unlock(target); ao2_unlink(candidates, target); target = ast_channel_unref(target); } ao2_ref(candidates, -1); return target; }
/*! * \brief Processes cleanup actions for a \ref event_session object. * * \internal * * \param session The event session object to cleanup (\ref event_session). */ static void event_session_cleanup(struct event_session *session) { if (!session) { return; } event_session_shutdown(session); ao2_unlink(event_session_registry, session); }
static pj_bool_t authenticate(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; ast_assert(endpoint != NULL); if (is_ack) { return PJ_FALSE; } if (ast_sip_requires_authentication(endpoint, rdata)) { pjsip_tx_data *tdata; struct unidentified_request *unid; pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata); switch (ast_sip_check_authentication(endpoint, rdata, tdata)) { case AST_SIP_AUTHENTICATION_CHALLENGE: /* Send the 401 we created for them */ ast_sip_report_auth_challenge_sent(endpoint, rdata, tdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_SUCCESS: /* See note in endpoint_lookup about not holding an unnecessary write lock */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } ast_sip_report_auth_success(endpoint, rdata); break; case AST_SIP_AUTHENTICATION_FAILED: log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; case AST_SIP_AUTHENTICATION_ERROR: log_failed_request(rdata, "Error to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); pjsip_tx_data_dec_ref(tdata); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } pjsip_tx_data_dec_ref(tdata); } else if (endpoint == artificial_endpoint) { /* Uh. Oh. The artificial endpoint couldn't challenge so block the request. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } return PJ_FALSE; }
static int queued_idle_thread_dead(void *data) { struct thread_worker_pair *pair = data; ao2_unlink(pair->pool->idle_threads, pair->worker); threadpool_send_state_changed(pair->pool); ao2_ref(pair, -1); return 0; }
int unpark_parked_user(struct parked_user *pu) { if (pu->lot) { ao2_unlink(pu->lot->parked_users, pu); parking_lot_remove_if_unused(pu->lot); return 0; } return -1; }
static void kqueue_timer_close(int handle) { struct kqueue_timer *our_timer; if (!(our_timer = lookup_timer(handle))) { return; } ao2_unlink(kqueue_timers, our_timer); ao2_ref(our_timer, -1); }
/*! * \internal * \since 12.0.0 * \brief If we are down to the last reference of a wrapper and it's still contained within the list, remove it from the list. * * \param wrapper reference to wait bridge wrapper being checked for list culling - will be cleared on exit */ static void wait_wrapper_removal(struct wait_bridge_wrapper *wrapper) { if (!wrapper) { return; } ao2_lock(wait_bridge_wrappers); if (ao2_ref(wrapper, 0) == 2) { /* Either we have the only real reference or else wrapper isn't in the container anyway. */ ao2_unlink(wait_bridge_wrappers, wrapper); } ao2_unlock(wait_bridge_wrappers); ao2_cleanup(wrapper); }
/*! * \internal * \brief Remove excess existing contacts that expire the soonest. * \since 13.18.0 * * \param contacts Container of unmodified contacts that could remove. * \param to_remove Maximum number of contacts to remove. * * \return Nothing */ static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts, unsigned int to_remove) { struct excess_contact_vector contact_vec; /* * Create a sorted vector to hold the to_remove soonest to * expire contacts. The vector has an extra space to * temporarily hold the longest to expire contact that we * won't remove. */ if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) { return; } ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add, &contact_vec); /* * The vector should always be populated with the number * of contacts we need to remove. Just in case, we will * remove all contacts in the vector even if the contacts * container had fewer contacts than there should be. */ ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove); to_remove = AST_VECTOR_SIZE(&contact_vec); /* Remove the excess contacts that expire the soonest */ while (to_remove--) { struct ast_sip_contact *contact; contact = AST_VECTOR_GET(&contact_vec, to_remove); ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to remove_existing\n", contact->uri, contact->aor); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact->uri, contact->aor, contact->user_agent); ao2_unlink(response_contacts, contact); } AST_VECTOR_FREE(&contact_vec); }
static void timerfd_timer_close(int handle) { struct timerfd_timer *our_timer, find_helper = { .handle = handle, }; if (handle == -1) { ast_log(LOG_ERROR, "Attempting to close timerfd handle -1"); return; } if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) { ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle); return; } ao2_unlink(timerfd_timers, our_timer); ao2_ref(our_timer, -1); }
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd) { int res; if (!ao2_ref_and_lock(schtd)) { return -1; } if (schtd->current_scheduler_id < 0 || schtd->interval <= 0) { ao2_unlock_and_unref(schtd); return 0; } schtd->interval = 0; ao2_unlock_and_unref(schtd); ao2_unlink(tasks, schtd); res = ast_sched_del(scheduler_context, schtd->current_scheduler_id); return res; }
int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback) { struct websocket_protocol *protocol; if (!(protocol = ao2_find(protocols, name, OBJ_KEY))) { return -1; } if (protocol->callback != callback) { ao2_ref(protocol, -1); return -1; } ao2_unlink(protocols, protocol); ao2_ref(protocol, -1); ast_verb(2, "WebSocket unregistered sub-protocol '%s'\n", name); return 0; }
/*! \brief Destroy function which unreserves/unreferences/removes a multiplexed thread structure */ static int multiplexed_bridge_destroy(struct ast_bridge *bridge) { struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt; ao2_lock(multiplexed_threads); multiplexed_thread->count -= 2; if (!multiplexed_thread->count) { ast_debug(1, "Unlinking multiplexed thread '%p' since nobody is using it anymore\n", multiplexed_thread); ao2_unlink(multiplexed_threads, multiplexed_thread); } multiplexed_nudge(multiplexed_thread); ao2_unlock(multiplexed_threads); ao2_ref(multiplexed_thread, -1); return 0; }
/* called whenever a channel is destroyed or a linkedid is changed to * potentially emit a CEL_LINKEDID_END event */ void ast_cel_check_retire_linkedid(struct ast_channel *chan) { const char *linkedid = ast_channel_linkedid(chan); char *lid; /* make sure we need to do all this work */ if (ast_strlen_zero(linkedid) || !ast_cel_track_event(AST_CEL_LINKEDID_END)) { return; } if (!(lid = ao2_find(linkedids, (void *) linkedid, OBJ_POINTER))) { ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n", linkedid); return; } /* We have a ref for each channel with this linkedid, the link and the above find, so if * before unreffing the channel we have a refcount of 3, we're done. Unlink and report. */ if (ao2_ref(lid, -1) == 3) { ao2_unlink(linkedids, lid); ast_cel_report_event(chan, AST_CEL_LINKEDID_END, NULL, NULL, NULL); } ao2_ref(lid, -1); }
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; }
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; }
static int astobj2_test_helper(int use_hash, int use_cmp, unsigned int lim, struct ast_test *test) { struct ao2_container *c1; struct ao2_container *c2; struct ao2_iterator it; struct test_obj *obj; struct test_obj tmp_obj; int bucket_size; int increment = 0; int destructor_count = 0; int num; int res = AST_TEST_PASS; /* This test needs at least 5 objects */ if (lim < 5) { lim = 5; } bucket_size = (ast_random() % ((lim / 4) + 1)) + 1; c1 = ao2_container_alloc(bucket_size, use_hash ? test_hash_cb : NULL, use_cmp ? test_cmp_cb : NULL); c2 = ao2_container_alloc(bucket_size, test_hash_cb, test_cmp_cb); if (!c1 || !c2) { ast_test_status_update(test, "ao2_container_alloc failed.\n"); res = AST_TEST_FAIL; goto cleanup; } /* Create objects and link into container */ destructor_count = lim; for (num = 1; num <= lim; num++) { if (!(obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor))) { ast_test_status_update(test, "ao2_alloc failed.\n"); res = AST_TEST_FAIL; goto cleanup; } snprintf(obj->c, sizeof(obj->c), "zombie #%d", num); obj->destructor_count = &destructor_count; obj->i = num; ao2_link(c1, obj); ao2_ref(obj, -1); if (ao2_container_count(c1) != num) { ast_test_status_update(test, "container did not link correctly\n"); res = AST_TEST_FAIL; } } ast_test_status_update(test, "Container created: random bucket size %d: number of items: %d\n", bucket_size, lim); /* Testing ao2_find with no flags */ num = 100; for (; num; num--) { int i = (ast_random() % ((lim / 2)) + 1); /* find a random object */ tmp_obj.i = i; if (!(obj = ao2_find(c1, &tmp_obj, 0))) { res = AST_TEST_FAIL; ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with no flags failed.\n", i); } else { /* a correct match will only take place when the custom cmp function is used */ if (use_cmp && obj->i != i) { ast_test_status_update(test, "object %d does not match object %d\n", obj->i, tmp_obj.i); res = AST_TEST_FAIL; } ao2_ref(obj, -1); } } /* Testing ao2_find with OBJ_POINTER */ num = 75; for (; num; num--) { int i = (ast_random() % ((lim / 2)) + 1); /* find a random object */ snprintf(tmp_obj.c, sizeof(tmp_obj.c), "zombie #%d", i); tmp_obj.i = i; if (!(obj = ao2_find(c1, &tmp_obj, OBJ_POINTER))) { res = AST_TEST_FAIL; ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_POINTER flag failed.\n", i); } else { /* a correct match will only take place when the custom cmp function is used */ if (use_cmp && obj->i != i) { ast_test_status_update(test, "object %d does not match object %d\n", obj->i, tmp_obj.i); res = AST_TEST_FAIL; } ao2_ref(obj, -1); } } /* Testing ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE. * In this test items are unlinked from c1 and placed in c2. Then * unlinked from c2 and placed back into c1. * * For this module and set of custom hash/cmp functions, an object * should only be found if the astobj2 default cmp function is used. * This test is designed to mimic the chan_iax.c call number use case. */ num = lim < 25 ? lim : 25; for (; num; num--) { if (!(obj = ao2_find(c1, NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE))) { if (!use_cmp) { ast_test_status_update(test, "ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE failed with default hash function.\n"); res = AST_TEST_FAIL; } } else { if (use_cmp) { ast_test_status_update(test, "ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE failed with custom hash function.\n"); res = AST_TEST_FAIL; } ao2_link(c2, obj); ao2_ref(obj, -1); } } it = ao2_iterator_init(c2, 0); while ((obj = ao2_iterator_next(&it))) { ao2_unlink(c2, obj); ao2_link(c1, obj); ao2_ref(obj, -1); } ao2_iterator_destroy(&it); /* Test Callback with no flags. */ increment = 0; ao2_callback(c1, 0, increment_cb, &increment); if (increment != lim) { ast_test_status_update(test, "callback with no flags failed. Increment is %d\n", increment); res = AST_TEST_FAIL; } /* Test Callback with OBJ_NODATA. This should do nothing different than with no flags here. */ increment = 0; ao2_callback(c1, OBJ_NODATA, increment_cb, &increment); if (increment != lim) { ast_test_status_update(test, "callback with OBJ_NODATA failed. Increment is %d\n", increment); res = AST_TEST_FAIL; } /* Is the container count what we expect after all the finds and unlinks?*/ if (ao2_container_count(c1) != lim) { ast_test_status_update(test, "container count does not match what is expected after ao2_find tests.\n"); res = AST_TEST_FAIL; } /* Testing iterator. Unlink a single object and break. do not add item back */ it = ao2_iterator_init(c1, 0); num = (lim / 4) + 1; while ((obj = ao2_iterator_next(&it))) { if (obj->i == num) { ao2_ref(obj, -1); ao2_unlink(c1, obj); break; } ao2_ref(obj, -1); } ao2_iterator_destroy(&it); /* Is the container count what we expect after removing a single item? */ if (ao2_container_count(c1) != (lim - 1)) { ast_test_status_update(test, "unlink during iterator failed. Number %d was not removed.\n", num); res = AST_TEST_FAIL; } /* Test unlink all with OBJ_MULTIPLE, leave a single object for the container to destroy */ ao2_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL); /* check to make sure all test_obj destructors were called except for 1 */ if (destructor_count != 1) { ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA failed. destructor count %d\n", destructor_count); res = AST_TEST_FAIL; } cleanup: /* destroy containers */ if (c1) { ao2_ref(c1, -1); } if (c2) { ao2_ref(c2, -1); } if (destructor_count > 0) { ast_test_status_update(test, "all destructors were not called, destructor count is %d\n", destructor_count); res = AST_TEST_FAIL; } else if (destructor_count < 0) { ast_test_status_update(test, "Destructor was called too many times, destructor count is %d\n", destructor_count); res = AST_TEST_FAIL; } return res; }