// Copyright (c) 2002 David Muse
// See the COPYING file for more information

#include <rudiments/hostentry.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>

// for ENOMEM
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXBUFFER	(32*1024)

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class hostentryprivate {
	friend class hostentry;
	private:
		hostent	*_he;
		#if defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) && \
				defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
			hostent		_hebuffer;
			char		*_buffer;
		#endif
};

// LAME: not in the class
#if (!defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) || \
	!defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R))
static mutex	*_hemutex;
#endif


hostentry::hostentry() {
	pvt=new hostentryprivate;
	pvt->_he=NULL;
	#if defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
		rawbuffer::zero(&pvt->_hebuffer,sizeof(pvt->_hebuffer));
		pvt->_buffer=NULL;
	#endif
}

hostentry::hostentry(const hostentry &h) {
	pvt=new hostentryprivate;
	initialize(h.getName());
}

hostentry &hostentry::operator=(const hostentry &h) {
	if (this!=&h) {
		initialize(h.getName());
	}
	return *this;
}

hostentry::~hostentry() {
	#if defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
		delete[] pvt->_buffer;
	#endif
	delete pvt;
}

const char *hostentry::getName() const {
	return pvt->_he->h_name;
}

const char * const *hostentry::getAliasList() const {
	return pvt->_he->h_aliases;
}

int hostentry::getAddressType() const {
	return pvt->_he->h_addrtype;
}

int hostentry::getAddressLength() const {
	return pvt->_he->h_length;
}

const char * const *hostentry::getAddressList() const {
	return pvt->_he->h_addr_list;
}

bool hostentry::needsMutex() {
	#if !defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) || \
		!defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
		return true;
	#else
		return false;
	#endif
}

void hostentry::setMutex(mutex *mtx) {
	#if !defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) || \
		!defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
		_hemutex=mtx;
	#endif
}

bool hostentry::initialize(const char *hostname) {
	return initialize(hostname,NULL,0,0);
}

bool hostentry::initialize(const char *address, int len, int type) {
	return initialize(NULL,address,len,type);
}

bool hostentry::initialize(const char *hostname, const char *address,
							int len, int type) {

	#if defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R)
		if (pvt->_he) {
			pvt->_he=NULL;
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
		}
		// gethostbyname_r is goofy.
		// It will retrieve an arbitrarily large amount of data, but
		// requires that you pass it a pre-allocated buffer.  If the
		// buffer is too small, it returns an ENOMEM and you have to
		// just make the buffer bigger and try again.
		int	errnop=0;
		for (int size=1024; size<MAXBUFFER; size=size+1024) {
			pvt->_buffer=new char[size];
			#if defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R_6) && \
				defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R_8)
			if (!((hostname)
				?(gethostbyname_r(hostname,
						&pvt->_hebuffer,
						pvt->_buffer,size,
						&pvt->_he,&errnop))
				:(gethostbyaddr_r(address,len,type,
						&pvt->_hebuffer,
						pvt->_buffer,size,
						&pvt->_he,&errnop)))) {
				return (pvt->_he!=NULL);
			}
			#elif defined(RUDIMENTS_HAVE_GETHOSTBYNAME_R_5) && \
				defined(RUDIMENTS_HAVE_GETHOSTBYADDR_R_7)
			if ((hostname)
				?(pvt->_he=gethostbyname_r(hostname,
						&pvt->_hebuffer,
						pvt->_buffer,size,
						&errnop))
				:(pvt->_he=gethostbyaddr_r(address,len,type,
						&pvt->_hebuffer,
						pvt->_buffer,size,
						&errnop))) {
				return true;
			}
			#endif
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
			pvt->_he=NULL;
			if (errnop!=ENOMEM) {
				return false;
			}
		}
		return false;
	#else
		pvt->_he=NULL;
		return (!(_hemutex && !_hemutex->lock()) &&
			((pvt->_he=((hostname)
				?gethostbyname(hostname)
				:gethostbyaddr(address,len,type)))!=NULL) &&
			!(_hemutex && !_hemutex->unlock()));
	#endif
}

char *hostentry::getAddressString(int index) const {
	size_t	addresslen=(getAddressLength()*4)+1;
	char	*address=new char[addresslen];
	address[0]='\0';
	for (int byte=0; byte<getAddressLength(); byte++) {
		snprintf(address,addresslen,
			"%s%d",address,getAddressList()[index][byte]);
		if (byte<getAddressLength()-1) {
			snprintf(address,addresslen,"%s.",address);
		}
	}
	return address;
}

bool hostentry::getAliasList(const char *hostname, char ***aliaslist) {
	hostentry	he;
	if (he.initialize(hostname)) {
		int	counter;
		for (counter=0; he.getAliasList()[counter]; counter++);
		char	**alias=new char *[counter+1];
		alias[counter]=NULL;
		for (int i=0; i<counter; i++) {
			alias[i]=charstring::duplicate(he.getAliasList()[i]);
		}
		*aliaslist=alias;
		return true;
	}
	return false;
}

bool hostentry::getAddressType(const char *hostname, int *addresstype) {
	hostentry	he;
	if (he.initialize(hostname)) {
		*addresstype=he.getAddressType();
		return true;
	}
	return false;
}

bool hostentry::getAddressLength(const char *hostname, int *addresslength) {
	hostentry	he;
	if (he.initialize(hostname)) {
		*addresslength=he.getAddressLength();
		return true;
	}
	return false;
}

bool hostentry::getAddressList(const char *hostname, char ***addresslist) {
	hostentry	he;
	if (he.initialize(hostname)) {
		int	counter;
		for (counter=0; he.getAddressList()[counter]; counter++);
		char	**addr=new char *[counter+1];
		addr[counter]=NULL;
		for (int i=0; i<counter; i++) {
			addr[i]=charstring::duplicate(he.getAddressList()[i]);
		}
		*addresslist=addr;
		return true;
	}
	return false;
}

bool hostentry::getAddressString(const char *hostname, int index,
							char **addressstring) {
	hostentry	he;
	if (he.initialize(hostname)) {
		*addressstring=he.getAddressString(index);
		return true;
	}
	return false;
}


bool hostentry::getName(const char *address, int len, int type, char **name) {
	hostentry	he;
	if (he.initialize(address,len,type)) {
		*name=charstring::duplicate(he.getName());
		return true;
	}
	return false;
}

bool hostentry::getAliasList(const char *address, int len, int type,
							char ***aliaslist) {
	hostentry	he;
	if (he.initialize(address,len,type)) {
		int	counter;
		for (counter=0; he.getAliasList()[counter]; counter++);
		char	**alias=new char *[counter+1];
		alias[counter]=NULL;
		for (int i=0; i<counter; i++) {
			alias[i]=charstring::duplicate(he.getAliasList()[i]);
		}
		*aliaslist=alias;
		return true;
	}
	return false;
}

bool hostentry::getAddressList(const char *address, int len, int type,
							char ***addresslist) {
	hostentry	he;
	if (he.initialize(address,len,type)) {
		int	counter;
		for (counter=0; he.getAddressList()[counter]; counter++);
		char	**addr=new char *[counter+1];
		addr[counter]=NULL;
		for (int i=0; i<counter; i++) {
			addr[i]=charstring::duplicate(he.getAddressList()[i]);
		}
		*addresslist=addr;
		return true;
	}
	return false;
}

bool hostentry::getAddressString(const char *address, int len, int type,
					int index, char **addressstring) {
	hostentry	he;
	if (he.initialize(address,len,type)) {
		*addressstring=he.getAddressString(index);
		return true;
	}
	return false;
}

void hostentry::print() const {

	if (!pvt->_he) {
		return;
	}

	printf("Name: %s\n",getName());
	printf("Alias list:\n");
	for (int i=0; getAliasList()[i]; i++) {
		printf("	%s\n",getAliasList()[i]);
	}
	printf("Address type: %d\n",getAddressType());
	printf("Address length: %d\n",getAddressLength());
	printf("Address list:\n");
	for (int i=0; getAddressList()[i]; i++) {
		char	*addr=getAddressString(i);
		printf("	%s\n",addr);
		delete[] addr;
	}
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1