/* $Id: dnscache.c,v 1.18 2000/07/07 18:03:42 jared Exp $ */

#include "config.h"

int cache_misses = 0, cache_hits = 0;
int dnsentries = 0;
time_t dnslog_last_log;

/*
 *
 */
char *get_ip(struct my_hostent *hp)
{
	static char local[20];
	if (hp == NULL)
	{
		return "NULL";
	}
	if (debug)
	{
		print_err(0, "hp->h_length = %d", hp->h_length);
		print_err(0, "hp->my_h_addr= %p", hp->my_h_addr);
	}
	snprintf(local, 20, "%d.%d.%d.%d", (unsigned char)hp->my_h_addr[0], 
		(unsigned char)hp->my_h_addr[1], (unsigned char)hp->my_h_addr[2],
		(unsigned char)hp->my_h_addr[3]);
	if (debug)
	{
		print_err(0, "%s", local);
	}

	return local;
}

/*
 *
 */
char *get_hostname(struct my_hostent *hp)
{
	static char local[256];
	struct hostent *localhp;

	memset(local, 0, 256);
	if (hp == NULL)
	{
		return "NULL";
	}

	localhp = gethostbyaddr(hp->my_h_addr, hp->h_length, AF_INET);
	if (localhp == NULL)
	{
		return(get_ip(hp));
	}

	strcpy(local, localhp->h_name);

	return local;
	
}

/*
 *
 */
void	expire_dns()
{
	struct dnscache *here = NULL;
	struct dnscache *last = NULL, *freethis = NULL;
	time_t now;
	int expired = 0;
	int count = 0;
	
	/* Check for a possible error case */
	if (dnshead == NULL)
		return;

	/* Count the elements in the dns cache */
	for (here=dnshead; here != NULL; here = here->next)
	{
		count++;
	}

	/* log the number of elements */
	if (debug)
	{
		print_err(0, "counted dns elements = %d", count);
	}
	
	/* Save the current time */
	time(&now);
	
	here = dnshead;

	while (here != NULL)
	{
		if (freethis != NULL)
		{
			if (freethis->hp != NULL)
			{
				/* free ->hp->h_name */
				free(freethis->hp->h_name);
				freethis->hp->h_name = NULL;

				/* free ->hp_>my_h_addr */
				FREE(freethis->hp->my_h_addr);
				freethis->hp->my_h_addr = NULL;
			
				/* free ->hp */
				FREE(freethis->hp);
				freethis->hp = NULL;
				
				/* free freethis->hostname */
				free(freethis->hostname);

				/* free * */
				FREE(freethis);
			}

			/* Count number of expired elements */
			expired++;

			/* decrement entries in the dns list accordingly */
			dnsentries--;

			/* set the fact we've got nothing to free() since
			   we just did it */

			freethis = NULL;
		}

		if (debug)
		{
			print_err(0, "Examining %s for dns cache expire - Difftime = %d", here->hostname, (now - here->lastquery));
		}
		if ((now - here->lastquery) > dnsexpire)
		{
			/* entry needs expiry */
			if (last == NULL)	/* set the new dnshead */
			{
				if (dnshead != NULL)
				{
					dnshead = here->next;
				}
			}
			else 
			{
				last->next = here->next;
				/* set the previous entry to point to the next
				   one so we can unlink the current entry */
			}

			/* tag the data for freeing */
			freethis = here;
		} else {
			last = here;
		}
		here = here->next;
	}

	if (freethis != NULL)
	{
		if (freethis->hp != NULL)
		{
			/* free ->hp->h_name */
			FREE(freethis->hp->h_name);
			freethis->hp->h_name = NULL;

			/* free ->hp_>my_h_addr */
			FREE(freethis->hp->my_h_addr);
			freethis->hp->my_h_addr = NULL;

			/* free ->hp */
			FREE(freethis->hp);
			freethis->hp = NULL;
		}
		FREE(freethis->hostname);

		/* free * */
		FREE(freethis);
		/* decrement entries in the dns list accordingly */
		dnsentries--;

		expired++;
		freethis = NULL;
	}

	if ((now - dnslog_last_log) > dnslog)
	{
		/* generate stats string */
		print_err(0,  "dnscache - %d hit %d miss %d exp %d count entr %d est entries",
			cache_hits, cache_misses, expired, count, dnsentries);
		/* Log it */

		time(&dnslog_last_log);
		
	}
}
	
/*
 *
 */
struct my_hostent	*do_my_gethostbyname(char *hostname)
{
	struct dnscache *newent;
	struct hostent hp;
	struct hostent *hp2;

	hp2 = gethostbyname(hostname);

	if (!hp2)
		return NULL;

	memset(&hp, 0, sizeof (struct hostent));
	memcpy(&hp, hp2, sizeof(struct hostent));

	if (!&hp)
		return NULL;

	/* allocate memory for the new entry */
	newent = MALLOC(sizeof(struct dnscache), "struct dnscache");

	memset(newent, 0, sizeof(struct dnscache));

	/* copy hostname over */
	newent->hostname = strdup(hostname);

	/* allocate memory for this entry */
	newent->hp = MALLOC(sizeof(struct my_hostent), "struct my_hostent");
	
	/* save the current time */
	time(&newent->lastquery);

	/* copy needed date to new hp thingie */

	newent->hp->h_name = strdup(hp.h_name);
	newent->hp->h_addrtype = hp.h_addrtype;
	newent->hp->h_length = hp.h_length;

	newent->hp->my_h_addr = MALLOC(hp.h_length, "hp.h_length");
	memset(newent->hp->my_h_addr, 0, hp.h_length);

	memcpy(newent->hp->my_h_addr, hp.h_addr, hp.h_length);

	newent->next = dnshead;
	dnshead = newent;
	cache_misses++;
	dnsentries++;
	return newent->hp;
}

/*
 *
 */
struct my_hostent *my_gethostbyaddr(const char *addr, int len, int type)
{
	return NULL;
}

/*
 *
 */
struct my_hostent *my_gethostbyname(char *hostname)
{
	struct dnscache *here;

	if (debug)
	{
		print_err(0, "Estimated number of entries in dns cache is %d", dnsentries);
	}

	expire_dns();

	for (here = dnshead; here != NULL; here = here->next)
	{
		if (strcmp(hostname, here->hostname) == 0)
		{
			cache_hits++;
			return here->hp;
		}
	}

	return do_my_gethostbyname(hostname);
}

