Esempio n. 1
0
/*
 * 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;
}
Esempio n. 2
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;
}
Esempio n. 3
0
static void pthread_timer_close(void *data)
{
	struct pthread_timer *timer = data;

	ao2_unlink(pthread_timers, timer);
	ao2_ref(timer, -1);
}
Esempio n. 4
0
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);
	}
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
/*!
 * \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);
	}
}
Esempio n. 7
0
/*! \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;
}
Esempio n. 8
0
/*!
 * \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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
/*!
 * \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);
}
Esempio n. 11
0
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;
}
Esempio n. 12
0
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;
}
Esempio n. 13
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;
}
Esempio n. 14
0
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);
}
Esempio n. 15
0
/*!
 * \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);
}
Esempio n. 16
0
/*!
 * \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);
}
Esempio n. 17
0
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);
}
Esempio n. 18
0
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;
}
Esempio n. 19
0
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;
}
Esempio n. 20
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;
}
Esempio n. 21
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);
}
Esempio n. 22
0
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;
}
Esempio n. 23
0
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;
}