/*******************************************************************************/
/*                                                                             */
/*  Copyright 2005 Pascal Gloor <pascal.gloor@spale.com>                       */
/*                                                                             */
/*  Licensed under the Apache License, Version 2.0 (the "License");            */
/*  you may not use this file except in compliance with the License.           */
/*  You may obtain a copy of the License at                                    */
/*                                                                             */
/*     http://www.apache.org/licenses/LICENSE-2.0                              */
/*                                                                             */
/*  Unless required by applicable law or agreed to in writing, software        */
/*  distributed under the License is distributed on an "AS IS" BASIS,          */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/*  See the License for the specific language governing permissions and        */
/*  limitations under the License.                                             */
/*                                                                             */
/*******************************************************************************/

#include "defs.h"
#include "lookup.h"


struct {
	char *name;
	char *opt1;
	char *opt2;
	char *opt3;
	char *path;
	regex_t reip;
	regex_t retxt;
} prog[3];

char *spath[] = {
	"/bin",
	"/sbin",
	"/usr/bin",
	"/usr/sbin",
	"/usr/local/sbin",
	"/usr/local/bin"
};

int init_resolver(void)
{
	char *string;
	int id;

	id = 2;

	prog[id].name = "nslookup";
	prog[id].opt1 = "-v";
	prog[id].opt2 = "-qt=A";
	prog[id].opt3 = "-qt=TXT";

	string = "internet address = (127\\.0\\.0\\.[[:digit:]]+)";

	if ( regcomp(&prog[id].reip, string, REG_EXTENDED) != 0 )
		return -1;

	string = "text = \"([^\"]*)\"";

	if ( regcomp(&prog[id].retxt, string, REG_EXTENDED) != 0 )
		return -1;

	id = 1;

	prog[id].name = "host";
	prog[id].opt1 = "-t";
	prog[id].opt2 = "A";
	prog[id].opt2 = "TXT";

	string = " has address (127\\.0\\.0\\.[[:digit:]]+)";

	if ( regcomp(&prog[id].reip, string, REG_EXTENDED) != 0 )
		return -1;

	string = "descriptive text \"([^\"]*)\"";

	if ( regcomp(&prog[id].retxt, string, REG_EXTENDED) != 0 )
		return -1;

	id = 0;

	prog[id].name = "dig";
	prog[id].opt1 = "--";
	prog[id].opt2 = "A";
	prog[id].opt3 = "TXT";

	string = " IN +A +(127\\.0\\.0\\.[[:digit:]]+)";

	if ( regcomp(&prog[id].reip, string, REG_EXTENDED) != 0 )
		return -1;

	string = "IN +TXT +\"([^\"]*)\"";

	if ( regcomp(&prog[id].retxt, string, REG_EXTENDED) != 0 )
		return -1;

	return 0;
}

int find_resolver(void)
{
	int i,j,k,l;

	j = sizeof spath / sizeof spath[0];
	l = sizeof prog / sizeof prog[0];

	for(k=0; k<l; k++)
		for(i=0; i<j; i++)
		{
			char fullname[MAXPATHLEN];
			int fd;

			sprintf(fullname,"%s/%s",spath[i],prog[k].name);

			if ( ( fd = open(fullname,O_RDONLY) ) >= 0 )
			{
				close(fd);
				prog[k].path = spath[i];
				return k;
			}
		}

	return -1;
}

struct blinfo_t *exec_resolver(int prg, uint32_t intip, char *ns)
{
	static struct blinfo_t blinfo;
	char cmd[MAXPATHLEN];
	char newip[256];
	struct in_addr addr;
	struct in_addr addrrev;
	int Afd[2], TXTfd[2], Apid=0, TXTpid=0;

	sprintf(cmd,"%s/%s",prog[prg].path,prog[prg].name);

	addr.s_addr = intip;

	addrrev.s_addr = 0;
	addrrev.s_addr |= ( ( addr.s_addr & 0xFF000000 ) >> 24 );
	addrrev.s_addr |= ( ( addr.s_addr & 0x00FF0000 ) >> 8 );
	addrrev.s_addr |= ( ( addr.s_addr & 0x0000FF00 ) << 8 );
	addrrev.s_addr |= ( ( addr.s_addr & 0x000000FF ) << 24 );

	sprintf(newip,"%s.%s",inet_ntoa(addrrev),ns);
/*
	printf("%s %s %s %s\n",cmd,prog[prg].opt1,prog[prg].opt2,newip);
*/

	pipe(Afd);
	pipe(TXTfd);

	if ( ( Apid = fork() ) == -1 )
	{
		fprintf(stderr,"cannot fork()\n");
		exit(EXIT_FAILURE);
	}
	else if ( Apid == 0 )
	{
		dup2(Afd[1], 1);
		close(2);
		open("/dev/null",O_WRONLY);
		execl(cmd, cmd, prog[prg].opt1, prog[prg].opt2, newip, NULL);
		exit(EXIT_FAILURE);
	}
	else
	{
		if ( ( TXTpid = fork() ) == -1 )
		{
			fprintf(stderr,"cannot fork()\n");
			exit(EXIT_FAILURE);
		}
		else if ( TXTpid == 0 )
		{
			dup2(TXTfd[1], 1);
			close(2);
			open("/dev/null",O_WRONLY);
			execl(cmd, cmd, prog[prg].opt1, prog[prg].opt3, newip, NULL);
			exit(EXIT_FAILURE);
		}
		else
		{
			ssize_t Alen = 65536;
			ssize_t TXTlen = 65536;
			char Abuf[65536];
			char TXTbuf[65536];
			int err,i;
			fd_set Aset,TXTset;
			struct timeval to;
			regmatch_t pmatch[2];

			FD_ZERO(&Aset);
			FD_ZERO(&TXTset);

			FD_SET(Afd[0], &Aset);
			FD_SET(TXTfd[0], &TXTset);

			to.tv_sec  = 1;
			to.tv_usec = 0;

			for(i=0; i<50; ++i)
			{
				int cexit;
				int pid;

				if ( ( pid = waitpid(-1, &cexit, WNOHANG) ) == 0 )
				{
					usleep(100000);
					continue;
				}

				if ( pid == Apid )
				{
					if ( WIFSIGNALED(cexit) )
					{
						fprintf(stderr,"child '%d' did not complete.\n",pid);
						errno=1;
						return NULL;
					}
					Apid = 0;
				}

				else if ( pid == TXTpid )
				{
					if ( WIFSIGNALED(cexit) )
					{
						fprintf(stderr,"child '%d' did not complete.\n",pid);
						errno=1;
						return NULL;
					}
					TXTpid = 0;
				}

				if ( Apid == 0 && TXTpid == 0 )
					break;
			}

			if ( Apid != 0 )
				kill(Apid, SIGKILL);

			if ( TXTpid != 0 )
				kill(TXTpid, SIGKILL);


			if ( select(Afd[0]+1, &Aset, NULL, NULL, &to) != 1 )
			{
				errno=0;
				return NULL;
			}

			if ( select(TXTfd[0]+1, &TXTset, NULL, NULL, &to) != 1 )
			{
				errno=0;
				return NULL;
			}

			Alen   = read(Afd[0],   Abuf,   Alen);
			TXTlen = read(TXTfd[0], TXTbuf, TXTlen);

			close(Afd[0]);
			close(Afd[1]);

			close(TXTfd[0]);
			close(TXTfd[1]);

			if ( Alen < 1 )
			{
				errno=1;
				return NULL;
			}

			if ( TXTlen < 0 )
			{
				errno=1;
				return NULL;
			}

			Abuf[Alen]     = '\0';
			TXTbuf[TXTlen] = '\0';

			if ( ( err = regexec(&prog[prg].reip, Abuf, 2, pmatch, 0) ) == 0 )
			{
				int iplen = pmatch[1].rm_eo - pmatch[1].rm_so;

				if ( iplen > 1 && iplen < BLLEN )
					memcpy(blinfo.ip,Abuf+pmatch[1].rm_so,iplen);
				else
					iplen = 0;

				blinfo.ip[iplen] = '\0';
				blinfo.txt[0] = '\0';

				if ( TXTlen > 0 && ( err = regexec(&prog[prg].retxt, TXTbuf, 2, pmatch, 0) ) == 0 )
				{
					int txtlen = pmatch[1].rm_eo - pmatch[1].rm_so;
	
					if ( txtlen > 1 && txtlen < BLLEN )
						memcpy(blinfo.txt,TXTbuf+pmatch[1].rm_so,txtlen);
					else
						txtlen = 0;
	
					blinfo.txt[txtlen] = '\0';
				}
/*
				else
				{
					regerror(err, &prog[prg].reip, TXTbuf, 65536);
					printf("regerror: %s\n",TXTbuf);
				}
*/
				return &blinfo;
			}
/*
			else
			{
				regerror(err, &prog[prg].reip, Abuf, 65536);
				printf("regerror: %s\n",Abuf);
				errno=0;
				return NULL;
			}
*/
		}

	}

	errno=0;
	return NULL;

}