#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef NETINET_SUPP_in
#include <netinet/in.h>
#endif
#include <netdb.h>
#include <setjmp.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include "core.h"
#include "netresolv.h"
enum {FALSE,TRUE};
const int RESOLVE_MAX1=RESOLVE_MAX - 1;
/*	NETRESOLV	- resolve INET addresses and return
			  the INET Hostnames

		Include a timeout of 60 sec. to resolve a name.
		If not resolved, discard the entry...

		Use STDIN to read from the SOCKETPAIR connection
		(i.e. don't care where it comes from...)
		Expect a struct to be sent in a specific format
		Return a struct to STDOUT in another format

*/

/* #define DEBUG 1  */
/* #define DDEBUG 1 */
extern int errno;
sigjmp_buf env;
#define SAVEMASK 1
int processone = 0;
int addedentries = 0;
int clearall = FALSE;
int oktocontinue = TRUE;
enum { START_SIG, STOP_SIG, JUMP_SIG, UNUSED};
enum { INIT, INSIDE_LOOP, WAITING_GETHOST, ADDING_ENTRIES, BUILD_RETURN };
int where_are_you = INIT;
int sig_in_spot = UNUSED;
int sig_time_spot = UNUSED;

FILE *dfp;

typedef void (*sigfunc) (int);

/*
   FOR RELIABLE SIGNAL HANDLING --- make sure a restart of
   system calls is performed when signals interrupt
 */
sigfunc
signal (int signo, sigfunc func)
{
  struct sigaction act, oact;

  act.sa_handler = func;
  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;
  if (signo != SIGALRM)
    act.sa_flags |= SA_RESTART;
  if (sigaction (signo, &act, &oact) < 0)
    return (SIG_ERR);
  return (oact.sa_handler);
}

struct nqueue 
{
	struct resolvaddr resentry;
	struct nqueue *link;
};

struct nqueue *last, *first;
struct nqueue dum;  /* Dummy record header */
#define MAXENTRIES 400
struct resolvaddr entries[MAXENTRIES];

char *dbgaddr(u_int32_t val)
{
	unsigned char *p;
	static char buf[40];  /* Worst case scenario for %u conversions */
	
	p = (unsigned char *)&val;
	sprintf(buf,"%u.%u.%u.%u",p[0],p[1],p[2],p[3]);
	return(buf);
}

void addentry( struct resolvaddr *entry )
{
	struct nqueue *place;
#ifdef DEBUG
	fprintf(dfp,"Adding entry...netresolv... \n");
	fflush(dfp);
#endif
	place = malloc(sizeof(*place));
	place->resentry = *entry;
#ifdef DEBUG
	fprintf(dfp,"Added entry...netresolv... %s\n",dbgaddr(entry->inetaddr));
	fflush(dfp);
#endif
	place->link = NULL;
	last->link = place;
	last = place;
	processone++;
}

void delentry()
{
	struct nqueue *current;

#ifdef DEBUG
	fprintf(dfp,"Deleted entry...netresolv clearall=%d\n",clearall);
#endif
	current = first->link;
	if (last==first->link)
		last = first;
	if (current)
	{
		first->link = current->link;
		free(current);
	}
	processone--;
}

void deleteall()
{
	struct nqueue *current, *h;

#ifdef DEBUG
	fprintf(dfp,"Deleted all...netresolv clearall=%d\n",clearall);
#endif
	current = first->link;
	while (current)
	{
		first->link = current->link;
		h = current;
		current = first->link;
		free(h);
	}
	processone=0;
	dum.link = NULL;
	last = first = &dum;
	
}

void sigincatch(int sig)
{
	int status;
	sigset_t sigset, oldmask;

	sigprocmask(0,NULL,&sigset);
	sigaddset(&sigset,sig);
	sigprocmask(SIG_BLOCK,&sigset,&oldmask);
	sig_in_spot = START_SIG;
#ifdef DEBUG
	fprintf(dfp,"SIGNAL CAUGHT\n");
	fflush(dfp);
#endif
	do
	{
	if (addedentries<MAXENTRIES)
	{
	read(0,&entries[addedentries],sizeof(struct resolvaddr));
	status = errno;
	    if (status != EWOULDBLOCK)
	    {
#ifdef DEBUG
		fprintf(dfp,"ADDING ENTRY\n");
		fflush(dfp);
#endif
		if (entries[addedentries].inetaddr == 0L)
		{
		    if (entries[addedentries].where == 0)
		    {
			clearall=TRUE;
#ifdef DDEBUG
		fprintf(dfp,"CLEARALL Issued, you are %d\n",where_are_you);
		fflush(dfp);
#endif
	/*		addedentries++; */
                    }
		    else if (entries[addedentries].where == (char *)1)
		    {
		        /* clearall=FALSE; */
#ifdef DDEBUG
		fprintf(dfp,"FINISHED Issued, not yet at CLEARALL false %d\n",where_are_you);
		fflush(dfp);
#endif
			addedentries++; 

		    }
		}
		else
		{
			addedentries++;
		}	
	    }
#ifdef DEBUG
	    else
	    {
		fprintf(dfp,"READ WOULD HAVE BLOCKED\n");
		fflush(dfp);
	    }
#endif
	}
	} while (status != EWOULDBLOCK && addedentries<MAXENTRIES);	
	sigprocmask(SIG_SETMASK,&oldmask,NULL);
	sig_in_spot = STOP_SIG;

}

void sigtimecatch(int sig)
{
	sig_time_spot = START_SIG;
#ifdef DEBUG
	fprintf(dfp,"TIMEOUT SIGNAL CAUGHT\n");
	fflush(dfp);
#endif
	sig_time_spot = STOP_SIG;
	siglongjmp(env,1);	
}

void setupsignals()
{
	signal(SIGALRM, sigtimecatch);
	signal(SIGUSR1, sigincatch);
	signal(SIGUSR2, SIG_IGN);
}

int main()
{
	struct nqueue *top;
	struct nqueue cptop;
	struct gotname finalentry;
	struct hostent *res;
	unsigned char *p;
	u_int32_t *pu;
	int i;

#ifdef DDEBUG
	dfp = fopen("dbugfile","w");
	fprintf(dfp,"Starting NETRESOLV\n");
	fflush(dfp);
#endif
	dum.link = NULL;
	last = &dum;
	first = &dum;
	fcntl(0,F_SETFL,FNDELAY);
	setupsignals();
	while (1)
	{
		if (addedentries)
		{
			where_are_you = ADDING_ENTRIES;
#ifdef DEBUG
			fprintf(dfp,"Loop to ADD %d entries\n",addedentries);
			fflush(dfp);
#endif
			signal(SIGUSR1,SIG_IGN);
			for (i=0;i<addedentries;i++)
				addentry(&entries[i]);
			addedentries = 0;
			signal(SIGUSR1,sigincatch);
		}
		if (processone)
		{
#ifdef DEBUG
			fprintf(dfp,"In processone section = %d\n",processone);
			fflush(dfp);
#endif
			top = first->link;
			if (top)
			{
			   cptop = *top;
			   if (sigsetjmp( env , SAVEMASK)==0)
			   {
				alarm(60);	
				p = (unsigned char *)&(cptop.resentry.inetaddr);
				pu = (u_int32_t *)p;
#ifdef DEBUG
			fprintf(dfp,"Actual Get host by address call\n");
			fflush(dfp);
#endif
				where_are_you = WAITING_GETHOST;		
				if (*pu != 0L)
				{
				   if (clearall)
					res = NULL;
				   else
					res = gethostbyaddr(p,
						sizeof(struct in_addr),AF_INET);
				}
				else
					res = (struct hostent *)1;
				alarm(0);
				where_are_you = BUILD_RETURN;
				/* Build return entry... */
				if (res)
				{
#ifdef DEBUG
					fprintf(dfp,"FINAL ENTRY = %p  RES=%p\n",cptop.resentry.where,res);
					fflush(dfp);
#endif
					finalentry.where = cptop.resentry.where;
					if (finalentry.where>(char *)1 &&
						res != (struct hostent *)1)
					strncpy(finalentry.name,res->h_name,RESOLVE_MAX1);
					else
					{
#ifdef DDEBUG
					fprintf(dfp,"SHOULD set clearall false\n");
					fflush(dfp);
#endif
						finalentry.name[0]=0;
						if (finalentry.where == 
							(char *)1)
							clearall = FALSE;
#ifdef DDEBUG
					else
				        {
					fprintf(dfp,"NEVER HAPPENS\n");
					fflush(dfp);
					}
#endif
						
					}
#ifdef DEBUG
					fprintf(dfp,"Returning %s\n",finalentry.name);
					fflush(dfp);
#endif
					finalentry.name[RESOLVE_MAX1] = 0;
					write(1,&finalentry,sizeof(finalentry));
				/* Send signal to indicate ALL is ready */
				kill(cptop.resentry.pid,SIGUSR2);
				}
#ifdef DEBUG
				else
				{
					fprintf(dfp,"Failed GETHOSTBYADDR with %d",errno);
					fflush(dfp);
				}
#endif
										
			   }
			   else
			   {
				/*  TIMED OUT */
#ifdef DEBUG
			fprintf(dfp,"Timed out\n");
			fflush(dfp);
#endif
				
				kill(cptop.resentry.pid,SIGUSR1);
			   }
			   delentry();			
			}
#ifdef DEBUG
			else
			{
			fprintf(dfp,"TOP NULL VALUE!!\n");
			fflush(dfp);
			
			}
#endif
		}
		else			
		   usleep(100);
		where_are_you = INSIDE_LOOP;
	}
	return(0);
}
