// Written by John Newbigin
// jn@it.swin.edu.au
// Copyright (c) 1999 John Newbigin
// Covered by the terms of the GPL.

#include "string.h"
#include "token.h"
#include "token2.h"
#include "tlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "version.h"

//#define CONFIG_PATH ""
#define CONFIG_PATH "/etc/"

#define NDU_CONFIG_FILE CONFIG_PATH"ndu.conf"
#define NAMED_CONFIG_FILE CONFIG_PATH"named.conf"

//#define IN_ADDR_ARPA ".IN-ADDR.ARPA"
#define IN_ADDR_ARPA ".in-addr.arpa"

extern void dnstouch(char *filename, HugeString *extra);

int verbose_level = 0;

class conf;

class entry
{
public:
	entry()
	{
		name = 0;
		address = 0;
		value = 0;
	}
	~entry()
	{
		if(name)
		{
			name->decRefCount();
			name = 0;
		}
		if(address)
		{
			address->decRefCount();
			address = 0;
		}
	}

	void dump()
	{
		printf("Host %30s  Address %s\n", name->pchar(), address->pchar());
	}

	void assignName(char *n)
	{
		if(name)
		{
			name->decRefCount();
			name = 0;
		}
		name = new HugeString();
		name->cat(n);
	}

	void assignAddress(char *a)
	{
		if(address)
		{
			address->decRefCount();
			address = 0;
		}
		address = new HugeString();
		address->cat(a);
	}

	HugeString *getAddress()
	{
		return address;
	}

	HugeString *getName()
	{
		return name;
	}

	int getValue()
	{
		return value;
	}

	void setValue(int v)
	{
		value = v;
	}
	
private:
	HugeString *name;
	HugeString *address;
	int value;
};

int compar(const void *a, const void *b)
{
	entry *e1 = ((entry **)a)[0];
	entry *e2 = ((entry **)b)[0];

	return e1->getValue() - e2->getValue();
}

class zone
{
public:
	conf *configuration;

	zone(conf *c, HugeString *fextra)
	{
		configuration = c;
		extra = fextra;
		file = 0;
		name = 0;
		fqfn = 0;
		zoneType = 0;
		fwd = new TList();
		rev = new TList();
		newRev = new TList();
	}

	~zone()
	{
		if(file)
		{
			file->decRefCount();
		}
		if(name)
		{
			name->decRefCount();
		}
		if(fqfn)
		{
			fqfn->decRefCount();
		}

		int i;
		
		for(i = 0; i<fwd->count(); i++)
		{
			entry *e = (entry *)fwd->get(i);
			delete e;
		}
		delete fwd;
		
		for(i = 0; i<rev->count(); i++)
		{
			entry *e = (entry *)rev->get(i);
			delete e;
		}
		delete rev;
		
		for(i = 0; i<newRev->count(); i++)
		{
			entry *e = (entry *)newRev->get(i);
			delete e;
		}
		delete newRev;
	}

	void scanZoneFile(char *filename);

	void assignFile(char *f)
	{
		if(file)
		{
			file->decRefCount();
			file = 0;
		}
		file = new HugeString();
		file->cat(f);
	}

	void assignName(char *n)
	{
		if(name)
		{
			name->decRefCount();
			name = 0;
		}
		name = new HugeString();
		name->cat(n);
	}

	HugeString *getName()
	{
		return name;
	}

	void dump()
	{
		printf("Master zone\n");
		if(name)
		{
			printf("Name %s\n", name->pchar());
		}
		if(file)
		{
			printf("File %s\n", file->pchar());
		}
		if(fwd->count() > 0)
		{
			printf("Address entries\n");
			for(int i=0; i<fwd->count(); i++)
			{
				((entry *)fwd->get(i))->dump();
			}
		}
		if(rev->count() > 0)
		{
			printf("Reverse pointers\n");
			for(int i=0; i<rev->count(); i++)
			{
				((entry *)rev->get(i))->dump();
			}
		}
	}

	void addFwdEntry(char *host, char *add);

	int getFwdCount()
	{
		return fwd->count();
	}

	entry *getFwdEntry(int i)
	{
		return (entry *)(fwd->get(i));
	}

	int getRevCount()
	{
		return rev->count();
	}
	
	entry *getRevEntry(int i)
	{
		return (entry *)(rev->get(i));
	}

	void addRevEntry(char *host, char *add)
	{
		entry *e = new entry();

		// remove "." from end of address
		int len = strlen(add);
		//printf("%d %s\n", len, add);
		if(len > 0 && add[len - 1] == '.')
		{
			add[len - 1] = 0;
		}

		// make host FQDN
		HugeString *h = new HugeString();
		h->cat(host);
		h->cat('.');
		h->cat(name->pchar());
		e->assignName(h->pchar());
		e->assignAddress(add);

		rev->add(e);

		delete h;
	}

	void addNewRevEntry(char *host, char *add)
	{
		entry *e = new entry();
		e->assignName(host);
		e->assignAddress(add);
		e->setValue(atoi(host));

		newRev->add(e);
	}

	void checkNewRev()
	{
		// we will check for duplicate reverse entries
		if(newRev->count() > 0)
		{
			for(int i = 0; i < newRev->count() - 1; i++)
			{
				entry *e1 = (entry *)newRev->get(i);
				entry *e2 = (entry *)newRev->get(i + 1);
				if(e1->getValue() == e2->getValue())
				{
					printf("Warning: %s and %s have the same IP address\n", e1->getAddress()->pchar(), e2->getAddress()->pchar());
					printf("Either remove the duplicate or add an ignore line to ndu.conf\n");
				}
			}
		}
	}

	void sortNewRev()
	{
		qsort(newRev->getBase(), newRev->count(), sizeof(void *), compar);
	}

	void dumpNewRev()
	{
		if(newRev->count() > 0)
		{
			printf("Reverse entries for zone %s\n", name->pchar());
			for(int i=0; i<newRev->count(); i++)
			{
				entry *e = (entry *)newRev->get(i);
				e->dump();
			}
		}
	}

	void writeNewRev()
	{
		if(newRev->count() > 0)
		{
			if(file && strlen(file->pchar()) > 0 && fqfn)
			{
				printf("Writing zone %s to file %s\n", name->pchar(), file->pchar());

				char buffer[1024];
				HugeString *newFileName = new HugeString();
				newFileName->cat(fqfn->pchar());
				newFileName->cat(".new");
				//printf("temp file name is %s\n", newFileName->pchar());

				FILE *newfile = fopen(newFileName->pchar(), "w");
				if(!newfile)
				{
					fprintf(stderr, "error creating new file\n");
					return;
				}
				FILE *oldfile = fopen(fqfn->pchar(), "r");
				if(!oldfile)
				{
					fprintf(stderr, "error opening zone file\n");
					return;
				}

				// set the new file security to be the same as the old file
				struct stat oldfilestat;
				stat(fqfn->pchar(), &oldfilestat);
				chown(newFileName->pchar(), oldfilestat.st_uid, oldfilestat.st_gid);
				chmod(newFileName->pchar(), oldfilestat.st_mode);
				// we should have checked all those return codes...

				int endHeader = 0;
				while(endHeader == 0)
				{
					if(fgets(buffer, sizeof(buffer), oldfile) == 0)
					{
						fprintf(stderr, "%s does not contain the end header tag\n", file->pchar());
						fprintf(stderr, "Either add \"ignore %s\" to ndu.conf or add the end header tag to the file\n", name->pchar());
						break;
					};
					if(strstr(buffer, "END<<HEADER>>") != 0)
					{
						endHeader = 1;
					}
					fprintf(newfile, buffer);
				}
				fclose(oldfile);

				if(endHeader)
				{

					// add the new entries
					for(int i=0; i<newRev->count(); i++)
					{
						entry *e = (entry *)newRev->get(i);
						fprintf(newfile, "%s\tIN\tPTR\t%s\n", e->getName()->pchar(), e->getAddress()->pchar());
					}
					fclose(newfile);

					dnstouch(newFileName->pchar(), extra);

					rename(newFileName->pchar(), fqfn->pchar());
				}

				delete newFileName;


			}
			else
			{
				//printf("There is no file to save zone %s too\n", name->pchar());
			}
		}
	}

	void process(char *directory)
	{
		if(name && file)
		{
			// see if file is a reverse file
			// if the zone ends in .in-addr.arpa
			if(strstr(name->pchar(), IN_ADDR_ARPA) > 0)
			{
				zoneType = 1;
			}

			// work out the file name
			fqfn = new HugeString();
			fqfn->cat(directory);
			fqfn->cat("/");
			fqfn->cat(file->pchar());
			//printf("Using file %s\n", fqfn->pchar());

			// scan the file
			scanZoneFile(fqfn->pchar());
		}
		else
		{
			fprintf(stderr, "Not enough information to parse zone\n");
		}
	}

private:
	HugeString *file;
	HugeString *name;
	HugeString *fqfn; // fully qualified file name
	HugeString *extra;

	int zoneType; // 0 = fwd, 1 = rev
	TList *fwd; // a list of entry objects
	TList *rev; // a list of entry objects
	TList *newRev; // a list of added new entry objects
};

class conf
{
public:
	conf()
	{
		directory = 0;
		chroot = 0;
		extra = 0;
		verbose = 0;
		if(verbose_level > 0)
		{
			verbose = 1;
		}
		zones = new TList();
		namedConfigFile = new HugeString();
		namedConfigFile->cat(NAMED_CONFIG_FILE);
		ignoreList = new TList();

		if(readConfigFile(NDU_CONFIG_FILE) == 1)
		{
			exit(1);
		}
	}

	~conf()
	{
		int i;
		for(i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			delete z;
		}
		delete zones;
	}

	int readConfigFile(char *filename);
	int parseNamedConf();

	void assignDirectory(char *d)
	{
		if(directory)
		{
			directory->decRefCount();
			directory = 0;
		}
		directory = new HugeString();
		if(chroot)
		{
			directory->cat(chroot->pchar());
		}
		directory->cat(d);
	}

	void addZone(zone *z)
	{
		zones->add(z);
	}

	void dump()
	{
		printf("Configuration:\n");
		if(directory)
		{
			printf("Directory %s\n", directory->pchar());
		};

		for(int i=0; i<zones->count(); i++)
		{
			((zone *)zones->get(i))->dump();
		}
	}

	int ignore(HugeString *str)
	{
		for(int j=0; j<ignoreList->count(); j++)
		{
			HugeString *s = (HugeString *)ignoreList->get(j);
			if(strcmp(s->pchar(), str->pchar()) == 0)
			{
				return 1;
			}
		}
		return 0;
	}

	void process()
	{
		for(int i=0; i<zones->count(); i++)
		{
			//int ignore = 0;
			// should we ignore this zone file....?
			// see if is an ignore zone
			HugeString *name = ((zone *)zones->get(i))->getName();

			if(!ignore(name))
			{
	//printf("Processing %s\n", name->pchar());
				((zone *)zones->get(i))->process(directory->pchar());
			}
		}
	}

	void buildNewTable()
	{
		// build a table of fwd addresses & hosts
		for(int i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			for(int j = 0; j < z->getFwdCount(); j++)
			{
				entry *e = z->getFwdEntry(j);

				// work out the rev zone
				HugeString *a1 = new HugeString();
				HugeString *a2 = new HugeString();
				HugeString *a3 = new HugeString();
				HugeString *a4 = new HugeString();

				// time for another FSM

				char *add = e->getAddress()->pchar();

				int i = 0;

				int state = 0;
				while(state != 9)
				{
					switch(state)
					{
						case 0:
							if(add[i] == 0)
							{
								state = 10; // error
							}
							else if(add[i] == '.')
							{
								state = 1;
							}
							else
							{
								a1->cat(add[i]); // we assume it is a valid char (ie a digit)
							}
							break;

						case 1:
							if(add[i] == 0)
							{
								state = 10 ; //error
							}
							else if(add[i] == '.')
							{
								state = 2;
							}
							else
							{
								a2->cat(add[i]);
							}
							break;

						case 2:
							if(add[i] == 0)
							{
								state = 10; // error
							}
							else if(add[i] == '.')
							{
								state = 3;
							}
							else
							{
								a3->cat(add[i]);
							}
							break;

						case 3:
							if(add[i] == 0)
							{
								state = 9;
							}
							else 
							{
								a4->cat(add[i]);
							}
							break;

						case 10:
							fprintf(stderr, "Error parsing for rev address\n");
							state = 9;
							break;
					}
					i++;
				}

				HugeString *zoneName = new HugeString();
				zoneName->cat(a3->pchar());
				zoneName->cat(".");
				zoneName->cat(a2->pchar());
				zoneName->cat(".");
				zoneName->cat(a1->pchar());
				zoneName->cat(IN_ADDR_ARPA);
				//printf("Looking up zone %s\n", zoneName->pchar());

				int found = 0;

				zone *z = 0;

				for(int k = 0; k < zones->count(); k++)
				{
					z = (zone *)zones->get(k);
					if(strcmp(z->getName()->pchar(), zoneName->pchar()) == 0)
					{
						//printf("Rev zone exists\n");
						found = 1;
						break;
					}
				}
				if(!found)
				{
					// see if is an ignore zone
					if(ignore(zoneName))
					{
						found = 1;
					}
					if(!found)
					{
						printf("zone %s not found.  If you are responsible for this zone, then please add it to named.conf\n", zoneName->pchar());
						printf(" If this zone is handled by another server, add \"ignore %s\" to %s\n", zoneName->pchar(), NDU_CONFIG_FILE);
					}
					// add the zone now.
					z = new zone(this, extra);
					z->assignName(zoneName->pchar());
					z->assignFile("");
					addZone(z);
				}

				// add the rev entry to the new rev list of zone z
				//printf("%s IN PTR %s.\n", a4->pchar(), e->getName()->pchar());

				HugeString *address = new HugeString();
				address->cat(e->getName()->pchar());
				address->cat(".");
				z->addNewRevEntry(a4->pchar(), address->pchar());
				address->decRefCount();

				a1->decRefCount();
				a2->decRefCount();
				a3->decRefCount();
				a4->decRefCount();
				zoneName->decRefCount();
				
			}
		}

		// for each entry, we work out the rev zone it belongs in.  We then use that to find the zone object and
		// add the reverse entry.

		// we should then check for non-existant zones
		// rev pointers for non-existant hosts
		// ???
	}

	void dumpNewTable()
	{
		// loop through the zones
		// any rev zones, dump the newrev list
		for(int i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			z->dumpNewRev();
		}
	}

	void sortNewTable()
	{
		// loop through the zones
		// any rev zones, dump the newrev list
		for(int i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			z->sortNewRev();
		}
	}
	
	void checkNewTable()
	{
		// loop through the zones
		// any rev zones, dump the newrev list
		for(int i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			z->checkNewRev();
		}
	}

	void save()
	{
		for(int i=0; i<zones->count(); i++)
		{
			zone *z = (zone *)zones->get(i);
			z->writeNewRev();
		}
	};
	
private:
	
	HugeString *directory;
	TList *zones;
	HugeString *namedConfigFile;
	TList *ignoreList;

	// path to prepend to zonf file locations
	HugeString *chroot;

	// extra characters to add to permitted word characters
	HugeString *extra;

	int verbose;
};


int conf::readConfigFile(char *filename)
{
	int error = 0;
	TToken *t = new TToken(0);
	if(t->AssignFile(filename))
	{
		//read the stuff
		HugeString *s = new HugeString();

		int state = 0;

		while(state != 9)
		{
			int type = t->getToken(s);
			if(type == 0)
			{
				state = 9; // eof
			}
			else if(type != 2)
			{
				switch(state)
				{
					case 0:
						if(strcmp(s->pchar(), "process") == 0)
						{
							state = 1; // read process file name
						}
						else if(strcmp(s->pchar(), "ignore") == 0)
						{
							state = 2; // read an ignore zone
						}
						else if(strcmp(s->pchar(), "chroot") == 0)
						{
							state = 4; // read the chroot directory
						}
						else if(strcmp(s->pchar(), "extra") == 0)
						{
							state = 5; // read extra word characters
						}
						else
						{
							fprintf(stderr, "Unknown option on line %d: %s (state=%d)\n", t->getLineNo(), s->pchar(), state);
							state = 9;
							error = 1;
						}
						break;

					case 1:
						namedConfigFile->clear();
						namedConfigFile->cat(s->pchar());
						//printf("got config %s\n", s->pchar());
						state = 0;
						break;

					case 2:
						{
							HugeString *zone = new HugeString();
							zone->cat(s->pchar());
							ignoreList->add(zone);
							//printf("got ignore %s\n", s->pchar());
							state = 0;
						}
						break;

					case 3:
						state = 0;
						break;

					case 4:
						{
							if(chroot)
							{
								chroot->decRefCount();
							}
							chroot = new HugeString();
							chroot->cat(s->pchar());
							//printf("got chroot %s\n", s->pchar());
							state = 0;
						}
						break;
						
					case 5:
						{
							if(extra)
							{
								extra->decRefCount();
							}
							extra = new HugeString();
							extra->cat(s->pchar());
							//printf("got extra %s\n", s->pchar());
							state = 0;
						}
						break;
				}
			}
		}
		
		delete s;	
	}
	delete t;
	return error;
}

int main(int argc, char **argv)
{
	printf("ndu version %s: DNS reverse file generator\nWritten by John Newbigin (jn@it.swin.edu.au)\n", NDU_VERSION);

	if(argc == 2 && strcmp(argv[1], "-v") == 0)
	{
		verbose_level++;
	}

	// assign default values
	conf *configuration = new conf();

	if(configuration->parseNamedConf())
	{
		configuration->process();
		//configuration->dump();
	
		configuration->buildNewTable();
		configuration->sortNewTable();
		//configuration->dumpNewTable();
		configuration->checkNewTable();

		// sort tables ?
		// fill in spaces ?
		// or better still, fill in table with defaults and then replace the named entries
	
		// what is the procedure for writing the new tables???
	
		// anything above the line ";;END<<HEADER>>" is preserved
		// this allows SOA and NS records
		// all the PTR records go below the line
		// anything there is deleted and the contents totaly re-created
		// if there are entries which are not created automaticly, they can be added above the line
	
		configuration->save();
	}

	/*
		By this stage, we have all the data that we need (I hope)

		We have to build a list of all the hosts with A records (fwd's)
		from this, we sort by address and create lists called nnn.nnn.nnn with addresses nnn
		we have to check if this file exists already.  If so, we re-write it, if not we generate a message

		we can have an option to fill in gaps in the name space
		the reverse files are auto generated.  do not edit them by hand
		
		I need to document the 'coding style' of the named files. 
	*/
	
}

int conf::parseNamedConf()
{
	TToken *t = new TToken(extra);
	if(!t->AssignFile(namedConfigFile->pchar()))
	{
		fprintf(stderr, "Could not open configuration file %s\n", namedConfigFile->pchar());
		return 0;
	}

	HugeString *s = new HugeString();
	// we will read through the file.
	// we could build a non-determinastic parser, or a recursive descent parser
	// but we will use an FSM for simplicity.
	// the config file is essentaly flat anyway

	// states 0 - nowhere
	//        1 - options
	//        2 - zone
	//        3 - unknown section
	//        4 - eof

	int state = 0;
	int depthCount = 0;

	int zoneType = 0; // 0 - unknown, 1 - master

	zone *currentZone = 0;

	while(state != 4)
	{
		int type=t->getToken(s);

		if(type == 0)
		{
			state = 4; // eof
		}
		else if(type != 2)
		{

			switch(state)
			{
				case 0: // nowhere
					if(strcmp(s->pchar(), "options") == 0)
					{
						depthCount = 0;
						state = 1;
						if(verbose)
						fprintf(stderr, "Found options section on line %d\n", t->getLineNo());
					}
					else if(strcmp(s->pchar(), "acl") == 0)
					{
						depthCount = 0;
						state = 1;
						if(verbose)
						fprintf(stderr, "Found acl section on line %d\n", t->getLineNo());
					}
					else if(strcmp(s->pchar(), "logging") == 0)
					{
						depthCount = 0;
						state = 1;
						if(verbose)
						fprintf(stderr, "Found logging section on line %d\n", t->getLineNo());
					}
					else if(strcmp(s->pchar(), "controls") == 0)
					{
						depthCount = 0;
						state = 1;
						if(verbose)
						fprintf(stderr, "Found controls section on line %d\n", t->getLineNo());
					}
					else if(strcmp(s->pchar(), "zone") == 0)
					{
						state = 2;
					}
					else if(strcmp(s->pchar(), "{") == 0)
					{
						depthCount++;
						state = 3;
					}
					else if(strcmp(s->pchar(), "}") == 0)
					{
						depthCount--;
						if(depthCount == 0)
						{
							state = 0;
						}
					}
					else if(strcmp(s->pchar(), ";") == 0)
					{
						// skip
					}
					else
					{
						if(verbose)
						{
							fprintf(stderr, "Possible error in config file, line %d\n", t->getLineNo());
							fprintf(stderr, "%s\n", s->pchar());
						}
						//state = 4;
						// ignore this stuff (it may be an error?)
					}
					break;

				case 1: // options
					if(strcmp(s->pchar(), "{") == 0)
					{
						depthCount++;
					}
					else if(strcmp(s->pchar(), "directory") == 0)
					{
						// we should check the depthCount
						state = 10; // we have found the directory
					}
					else if(strcmp(s->pchar(), "}") == 0)
					{
						depthCount--;
						if(depthCount == 0)
						{
							state = 0;
						}
					}
					else
					{
						// just skip this, we don't care about it
					}
					break;
	
				case 2: // zone
					zoneType = 0;
					if(verbose)
						fprintf(stderr, "Zone name %s\n", s->pchar());
					currentZone = new zone(this, extra);
					currentZone->assignName(s->pchar());
					state = 20;
					break;
	
				case 3: // unknown section
					break;
	
				case 4: // eof
					break;

				case 10: // found directory
					if(verbose)
						fprintf(stderr, "The directory is %s\n", s->pchar());
					//configuration->assignDirectory(s->pchar());
					assignDirectory(s->pchar());
					state = 1;
					break;

				case 20: // zone contents
					if(strcmp(s->pchar(), "{") == 0)
					{
						depthCount++;
					}
					else if(strcmp(s->pchar(), "type") == 0)
					{
						// we should check the depthCount
						state = 21; // we have found the type 
					}
					else if(strcmp(s->pchar(), "file") == 0)
					{
						state = 22; // we have found the file
					}
					else if(strcmp(s->pchar(), "}") == 0)
					{
						depthCount--;
						if(depthCount == 0)
						{
							// add the zone to the list of zones....
							if(zoneType == 1)
							{
								//configuration->addZone(currentZone);
								addZone(currentZone);
								currentZone = 0;
							}
							else
							{
								delete currentZone;
								currentZone = 0;
							}
							state = 0;
						}
					}
					else
					{
						// just skip this, we don't care about it
					}
					break;
					
				case 21: // read the zone type
					if(verbose)
						fprintf(stderr, "zone type %s\n", s->pchar());
					if(strcmp("master", s->pchar()) == 0)
					{
						zoneType = 1;
					}
					state = 20;
					break;

				case 22: // we have found the zone file
					if(verbose)
						fprintf(stderr, "zone file %s\n", s->pchar());
					if(currentZone)
					{
						currentZone->assignFile(s->pchar());
					}
					state = 20;
					break;

					
				default:
					fprintf(stderr, "Unknown state %d\n", state);
					state = 4;
					break;
			}
		}
	}
	delete s;
	delete t;

	return 1;
};

void zone::scanZoneFile(char *filename)
{
	TToken2 *t = new TToken2(extra);
	if(!t->AssignFile(filename))
	{
		fprintf(stderr, "Could not open zone file %s, skipping\n", filename);
		return;
	}
	

	HugeString *s     = new HugeString();
	HugeString *saved = new HugeString();

	// States:
	// 0 - nowhere
	// 1 - start of a record
	// 2 - address record
	// 3 - pointer record
	// 5 - bracket set

	// 9 - eof

	int state = 0;
	while(state != 9)
	{
		int type = t->getToken(s);
		if(type == 0)
		{
			state = 9;
		}
		if(type != 2)
		{
			switch(state)
			{
				case 0:
					if(strcmp(s->pchar(), "(") == 0)
					{
						state =  5;
					}
					else if(strcmp(s->pchar(), "IN") == 0)
					{
						// skip this
					}
					else if(strcmp(s->pchar(), "A") == 0)
					{
						state = 2;
					}
					else if(strcmp(s->pchar(), "PTR") == 0)
					{
						state = 3;
					}
					else
					{
						HugeString *temp;
						temp  = saved;
						saved = s;
						s     = temp;
					}
					break;

				case 2:
					// the host name is saved
					// read the address
					//printf("Host %s.%s address %s\n", saved->pchar(), name->pchar(), s->pchar());
					addFwdEntry(saved->pchar(), s->pchar());
					state = 0;
					break;

				case 3:
					//printf("address %s host %s\n", saved->pchar(), s->pchar());
					addRevEntry(saved->pchar(), s->pchar());
					state = 0;
					break;

				case 5:
					if(strcmp(s->pchar(), ")") == 0)
					{
						state = 0;
					}
					break;
			}
		}
	}
	
	return;
}

void zone::addFwdEntry(char *host, char *add)
{

	// make host FQDN
	HugeString *h = new HugeString();
	h->cat(host);
	h->cat('.');
	h->cat(name->pchar());

	// see if this is an ignore...
	if(configuration->ignore(h))
	{
		//printf("Ignore %s\n", h->pchar());
	}
	else
	{
		entry *e = new entry();
		e->assignName(h->pchar());
		e->assignAddress(add);

		fwd->add(e);
	}

	delete h;
}

