Exemple #1
0
static void * cache_maintain(void * arg)
{
	/* This code runs as a thread and is responsible for maintaining the
	 * cache. Every 10 seconds it scans 1/6 of the cache, so that in a
	 * minute it will scan the entire cache. It attempts to refresh cache
	 * entries which have expired, but only up to 5 times if they have not
	 * been used in the interim. After that they are removed. */
	int entries = HASH_SIZE / 6;
	int position = 0;
	for(;;)
	{
		int i;
		time_t now;
		struct timespec delay;
		
		delay.tv_sec = 10;
		delay.tv_nsec = 0;
		while(nanosleep(&delay, &delay) < 0 && errno == EINTR)
			if(debug)
				printf("Resuming interrupted sleep!\n");
		
		pthread_mutex_lock(&cache_mutex);
		if(debug)
			printf("Look over 1/6 of cache...\n");
		now = time(NULL);
		for(i = 0; i < entries; i++)
		{
			/* Since we'll potentially be removing entries from the
			 * linked list, we keep a pointer to the previous
			 * element's link to us so that we can update it and use
			 * that to get to the next element if we remove one. */
			struct cache_entry ** point = &hash_table[position];
			struct cache_entry * scan;
			while((scan = *point))
			{
				/* GET*ENT entries do not get refreshed here */
				if(scan->refreshes == 5 || (now > scan->expire_time &&
				   (scan->type == GETPWENT || scan->type == GETGRENT)))
					/* kill it */
					cache_entry_destroy(scan);
				else if(now > scan->expire_time)
				{
					request_header req = {version: NSCD_VERSION, type: scan->type, key_len: scan->key_len};
					int r;
					void * reply;
					int32_t reply_len;
					time_t refresh_interval;
					
					/* refresh it */
					if(debug)
						printf("Refreshing cache entry for [%s], refreshes %d\n", (char *) scan->key, scan->refreshes);
					pthread_mutex_unlock(&cache_mutex);
					r = generate_reply(&req, scan->key, -1, &reply, &reply_len, &refresh_interval);
					pthread_mutex_lock(&cache_mutex);
					now = time(NULL);
					
					/* while we were refreshing it, it may have
					 * been marked stale and a new copy fetched */
					if(scan->refreshes == 5 || r < 0)
					{
						/* kill it */
						cache_entry_destroy(scan);
						if(r >= 0)
							free(reply);
					}
					else
					{
						free(scan->reply);
						scan->reply = reply;
						scan->reply_len = reply_len;
						scan->expire_time += refresh_interval;
						scan->refresh_interval = refresh_interval;
						scan->refreshes++;
						/* go to next entry */
						point = &scan->chain;
					}
				}
				else
					/* go to next entry */
					point = &scan->chain;
			}
			if(++position == HASH_SIZE)
				position = 0;
		}
Exemple #2
0
static void
process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
	ns_lwdclient_t *client = ev->ev_arg;
	isc_eventtype_t evtype;
	isc_boolean_t claimed;

	ns_lwdclient_log(50, "find done for task %p, client %p", task, client);

	evtype = ev->ev_type;
	isc_event_free(&ev);

	/*
	 * No more info to be had?  If so, we have all the good stuff
	 * right now, so we can render things.
	 */
	claimed = ISC_FALSE;
	if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
		if (NEED_V4(client)) {
			client->v4find = client->find;
			claimed = ISC_TRUE;
		}
		if (NEED_V6(client)) {
			client->v6find = client->find;
			claimed = ISC_TRUE;
		}
		if (client->find != NULL) {
			if (claimed)
				client->find = NULL;
			else
				dns_adb_destroyfind(&client->find);

		}
		generate_reply(client);
		return;
	}

	/*
	 * We probably don't need this find anymore.  We're either going to
	 * reissue it, or an error occurred.  Either way, we're done with
	 * it.
	 */
	if ((client->find != client->v4find)
	    && (client->find != client->v6find)) {
		dns_adb_destroyfind(&client->find);
	} else {
		client->find = NULL;
	}

	/*
	 * We have some new information we can gather.  Run off and fetch
	 * it.
	 */
	if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
		restart_find(client);
		return;
	}

	/*
	 * An error or other strangeness happened.  Drop this query.
	 */
	cleanup_gabn(client);
	ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
}
Exemple #3
0
static void
restart_find(ns_lwdclient_t *client) {
	unsigned int options;
	isc_result_t result;
	isc_boolean_t claimed;

	ns_lwdclient_log(50, "starting find for client %p", client);

	/*
	 * Issue a find for the name contained in the request.  We won't
	 * set the bit that says "anything is good enough" -- we want it
	 * all.
	 */
	options = 0;
	options |= DNS_ADBFIND_WANTEVENT;
	options |= DNS_ADBFIND_RETURNLAME;

	/*
	 * Set the bits up here to mark that we want this address family
	 * and that we do not currently have a find pending.  We will
	 * set that bit again below if it turns out we will get an event.
	 */
	if (NEED_V4(client))
		options |= DNS_ADBFIND_INET;
	if (NEED_V6(client))
		options |= DNS_ADBFIND_INET6;

 find_again:
	INSIST(client->find == NULL);
	result = dns_adb_createfind(client->clientmgr->view->adb,
				    client->clientmgr->task,
				    process_gabn_finddone, client,
				    dns_fixedname_name(&client->target_name),
				    dns_rootname, 0, options, 0,
				    dns_fixedname_name(&client->target_name),
				    client->clientmgr->view->dstport,
				    &client->find);

	/*
	 * Did we get an alias?  If so, save it and re-issue the query.
	 */
	if (result == DNS_R_ALIAS) {
		ns_lwdclient_log(50, "found alias, restarting query");
		dns_adb_destroyfind(&client->find);
		cleanup_gabn(client);
		result = add_alias(client);
		if (result != ISC_R_SUCCESS) {
			ns_lwdclient_log(50,
					 "out of buffer space adding alias");
			ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
			return;
		}
		goto find_again;
	}

	ns_lwdclient_log(50, "find returned %d (%s)", result,
			 isc_result_totext(result));

	/*
	 * Did we get an error?
	 */
	if (result != ISC_R_SUCCESS) {
		if (client->find != NULL)
			dns_adb_destroyfind(&client->find);
		cleanup_gabn(client);
		ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
		return;
	}

	claimed = ISC_FALSE;

	/*
	 * Did we get our answer to V4 addresses?
	 */
	if (NEED_V4(client)
	    && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
		ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
				 client, client->find);
		claimed = ISC_TRUE;
		client->v4find = client->find;
	}

	/*
	 * Did we get our answer to V6 addresses?
	 */
	if (NEED_V6(client)
	    && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
		ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
				 client, client->find);
		claimed = ISC_TRUE;
		client->v6find = client->find;
	}

	/*
	 * If we're going to get an event, set our internal pending flag
	 * and return.  When we get an event back we'll do the right
	 * thing, basically by calling this function again, perhaps with a
	 * new target name.
	 *
	 * If we have both v4 and v6, and we are still getting an event,
	 * we have a programming error, so die hard.
	 */
	if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
		ns_lwdclient_log(50, "event will be sent");
		INSIST(client->v4find == NULL || client->v6find == NULL);
		return;
	}
	ns_lwdclient_log(50, "no event will be sent");
	if (claimed)
		client->find = NULL;
	else
		dns_adb_destroyfind(&client->find);

	/*
	 * We seem to have everything we asked for, or at least we are
	 * able to respond with things we've learned.
	 */

	generate_reply(client);
}
Exemple #4
0
/* Return values:
 * Negative on error
 * 0 on success with a reusable socket
 * 1 on success with a non-reusable socket */
static int process_request(int client, uid_t uid, request_header * req, void * key)
{
	struct cache_entry * entry;
	void * reply;
	int32_t reply_len;
	time_t refresh_interval;
	pthread_mutex_t * extra_mutex = NULL;
	int r;
	
	if(debug)
		printf("Got request type %d (key = [%s]) from UID %d on FD %d\n", req->type, (char *) key, uid, client);
	/* first check for control messages */
	if(req->type > LASTDBREQ && req->type != GETPWENT && req->type != GETGRENT && req->type != GETAI && req->type != INITGROUPS)
	{
		if(req->type == SHUTDOWN)
			exit(0);
		if(req->type == GETSTAT)
		{
			/* to aid the use of -g in figuring out whether
			 * gnscd is answering queries correctly, grab
			 * the cache mutex and release it to make sure
			 * it's not stuck locked by some thread */
			pthread_mutex_lock(&cache_mutex);
			pthread_mutex_unlock(&cache_mutex);
			send_stats(client, uid);
		}
		if(req->type == INVALIDATE)
		{
			/* key is "passwd" "group" or "hosts" */
			/* use memcmp not strcmp for security */
			/* if it's hosts, call res_init() */
			/* NOT IMPLEMENTED */
		}
		return 1;
	}
	
	/* not a control message, so check if the service is disabled */
	if(is_disabled(req->type))
	{
		if(debug)
			printf("Service type %d disabled\n", req->type);
		r = generate_disabled_reply(req->type, &reply, &reply_len);
		if(r < 0)
			return -1;
		if(write_all(client, reply, reply_len, SHORT_TIMEOUT) != reply_len)
			return -1;
		return r;
	}
	
	/* if it is a GET*ENT query, grab the extra mutex */
	if(req->type == GETPWENT)
		extra_mutex = pwent_query_mutex;
	else if(req->type == GETGRENT)
		extra_mutex = grent_query_mutex;
	if(extra_mutex)
		pthread_mutex_lock(extra_mutex);
	
	pthread_mutex_lock(&cache_mutex);
	r = cache_search(req, key, uid, &entry);
	if(r < 0 && extra_mutex)
	{
		if(debug)
			printf("Not in the cache, requesting iteration.\n");
		/* request it */
		r = request_ent_cache(req, key, uid);
		if(r >= 0)
			r = cache_search(req, key, uid, &entry);
	}
	if(r >= 0)
	{
		if(debug)
			printf("Found it in the cache!\n");
		r = entry->close_socket;
		/* reset the refresh count */
		entry->refreshes = 0;
		if(write_all(client, entry->reply, entry->reply_len, SHORT_TIMEOUT) != entry->reply_len)
			r = -1;
		pthread_mutex_unlock(&cache_mutex);
		if(extra_mutex)
			pthread_mutex_unlock(extra_mutex);
		return r;
	}
	pthread_mutex_unlock(&cache_mutex);
	
	if(debug)
		printf("Not in the cache.\n");
	
	if(!extra_mutex)
		/* find it */
		r = generate_reply(req, key, uid, &reply, &reply_len, &refresh_interval);
	else
		r = -1;
	
	if(r >= 0)
	{
		int close_socket = r;
		int add_result = -1;
		
		if(write_all(client, reply, reply_len, SHORT_TIMEOUT) != reply_len)
		{
			if(debug)
				printf("Failed to write to client %d\n", client);
			r = -1;
		}
		
		pthread_mutex_lock(&cache_mutex);
		/* don't add duplicate entries */
		if(cache_search(req, key, uid, &entry) < 0)
			add_result = cache_add(req, key, uid, reply, reply_len, close_socket, refresh_interval);
		if(add_result < 0)
			/* either it was already in the cache or adding it failed */
			free(reply);
		pthread_mutex_unlock(&cache_mutex);
	}
	
	/* if it was a GET*ENT query, release the extra mutex */
	if(extra_mutex)
		pthread_mutex_unlock(extra_mutex);
	
	return r;
}