/* Hostloop -- the Hostloop Library
 * Copyright (C) 1998 Renaud Deraison
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */   

/*
		host.c, modified by Renaud Deraison <deraison@worldnet.fr>
		for the hostloop library.
		
		
		This source is ___very___ messy, and I'll clean
		it when I have some time... It currently works, 
		so I don't touch it...
		
		
     
		
Original copyright :
 * ++Copyright++ 1986
 * -
 * Copyright (c) 1986
 *    The Regents of the University of California.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 * 	This product includes software developed by the University of
 * 	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * -
 * --Copyright--
 */


#include <includes.h>
#ifndef NESSUSNT
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1986 Regents of the University of California.\n\
 portions Copyright (c) 1993 Digital Equipment Corporation\n\
 All rights reserved.\n";
#endif /* not lint */

/*
 * Actually, this program is from Rutgers University, however it is 
 * based on nslookup and other pieces of named tools, so it needs
 * that copyright notice.
 */

#ifndef lint
/* static char rcsid[] = "$Id: host.c,v 8.8 1995/12/06 20:34:52 vixie Exp $"; */
#endif /* not lint */


#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "hosts_loop.h"




#ifndef INT16SZ
#define INT16SZ 2
#endif

#ifndef INT32SZ
#define INT32SZ 4
#endif

#ifndef INADDRSZ
#define INADDRSZ 4
#endif

#ifndef HFIXEDSZ
#define HFIXEDSZ 12
#endif
extern int h_errno;


#define NUMMX 50

#define  SUCCESS		0
#define  TIME_OUT		-1
#define  NO_INFO 		-2
#define  ERROR 			-3
#define  NONAUTH 		-4

#define NAME_LEN 256

#ifndef T_TXT
#define T_TXT 16
#endif
#ifndef NO_DATA
#define NO_DATA NO_ADDRESS
#endif
#ifndef C_HS
#define C_HS 4
#endif

int sockFD;
FILE *filePtr;
const char *DecodeError(int);


static char *cname = NULL;
int getclass = C_IN;
int gettype;
int list = 0;
int server_specified = 0;

u_char *pr_cdname();
char* pr_rr();
int ListHosts();



static struct hostlist_t ** _hls_host_ghl;
short get_domain_computers(char * domain, struct hostlist_t *hl)
{

	struct hostent *hp;

	
	res_init();
	_res.retrans = 5;
	_hls_host_ghl = &hl;
	list++;
	hp = NULL;
	h_errno = TRY_AGAIN;
	_res.options &= ~RES_DEFNAMES;
	return(ListHosts(domain, T_A, hl));

}




typedef union {
	HEADER qb1;
	u_char qb2[PACKETSZ];
} querybuf;

static u_char hostbuf[BUFSIZ+1];


int printinfo(answer, eom, filter, isls)
	querybuf *answer;
	u_char *eom;
        int filter;
        int isls;
{
	HEADER *hp;
	u_char *bp, *cp;
	int  nmx, ancount, nscount, arcount, qdcount, buflen;

	/*
	 * find first satisfactory answer
	 */
	hp = (HEADER *) answer;
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	nscount = ntohs(hp->nscount);
	arcount = ntohs(hp->arcount);
	if (hp->rcode != NOERROR || (ancount+nscount+arcount) == 0) {
		switch (hp->rcode) {
			case NXDOMAIN:
				h_errno = HOST_NOT_FOUND;
				return(0);
			case SERVFAIL:
				h_errno = TRY_AGAIN;
				return(0);
#ifdef OLDJEEVES
			/*
			 * Jeeves (TOPS-20 server) still does not
			 * support MX records.  For the time being,
			 * we must accept FORMERRs as the same as
			 * NOERROR.
			 */
			case FORMERR:
#endif /*OLDJEEVES*/
			case NOERROR:
/* TpB - set a return error for this case. NO_DATA */
				h_errno = NO_DATA;
				return(0); /* was 1,but now indicates exception */
#ifndef OLDJEEVES
			case FORMERR:
#endif /*OLDJEEVES*/
			case NOTIMP:
			case REFUSED:
				h_errno = NO_RECOVERY;
				return(0);
		}
		return (0);
	}
	bp = hostbuf;
	nmx = 0;
	buflen = sizeof(hostbuf);
	cp = (u_char *)answer + HFIXEDSZ;
	if (qdcount) {
		cp += dn_skipname(cp, eom) + QFIXEDSZ;
		while (--qdcount > 0)
			cp += dn_skipname(cp, eom) + QFIXEDSZ;
	}
	if (ancount) {
	  
	  while (--ancount >= 0 && cp && cp < eom) {
	    cp = (u_char *)pr_rr(cp, answer, stdout, filter, _hls_host_ghl);
/*
 * When we ask for address and there is a CNAME, it seems to return
 * both the CNAME and the address.  Since we trace down the CNAME
 * chain ourselves, we don't really want to print the address at
 * this point.
 */
	    if (cname)
	      return (1);
	  }
	}
	if (0)
	  return (1);
	if (nscount) {
	  while (--nscount >= 0 && cp && cp < eom) {
	    cp = (u_char *)pr_rr(cp, answer, stdout, filter, _hls_host_ghl);
	  }
	}
	if (arcount) {
	  while (--arcount >= 0 && cp && cp < eom) {
	    cp = (u_char *)pr_rr(cp, answer, stdout, filter, _hls_host_ghl);
	  }
	}
	return(1);
 }


static char cnamebuf[MAXDNAME];
/*
 * Print resource record fields in human readable form.
 */
char *
pr_rr(cp, msg, file, filter,hl)
	u_char *cp, *msg;
	FILE *file;
        int filter;
        struct hostlist_t **hl;
{
	int type, class, dlen, ttl,n;
	struct in_addr inaddr;
	u_char *cp1;
	char name[MAXDNAME];

	if ((cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name))) == NULL)
		return (NULL);			/* compression error */

	type = _getshort(cp);
	cp += INT16SZ;

	class = _getshort(cp);
	cp += INT16SZ;

	ttl = _getlong(cp);
	cp += INT32SZ;

	
	dlen = _getshort(cp);
	cp += INT16SZ;
	cp1 = cp;
	/*
	 * Print type specific data, if appropriate
	 */
	
	if(type == T_A)
	{
	bcopy(cp, (char *)&inaddr, INADDRSZ);
	(*hl)->hostname = (char *)malloc(strlen(name)+1);
	bzero((*hl)->hostname,strlen(name)+1);
	strncpy((*hl)->hostname, name, strlen(name));
	(*hl)->hostip = inaddr;
	(*hl)->next = (struct hostlist_t*)malloc(sizeof(struct hostlist_t));
	bzero((*hl)->next, sizeof(struct hostlist_t));
#ifdef DEBUGMORE
	fprintf(stderr, "%s (%s)\n", (*hl)->hostname, inet_ntoa((*hl)->hostip));
#endif
	*hl = (*hl)->next;
	}
	if(type == T_HINFO)
	{
		
			u_char *cp2 = cp + dlen;
			if ((n = *cp++)) {
				if (1);
				 
				cp += n;
			}
			if ((cp < cp2) && (n = *cp++)) {
				if (1);
				
				cp += n;
			} else if (type == T_HINFO)
				if (1);
	}
	if(type == T_CNAME)
	{
		if (dn_expand(msg, msg + 512, cp, cnamebuf, 
			      sizeof(cnamebuf)) >= 0)
			cname = cnamebuf;
			cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (1);
	}
		
	return(NULL);
}






u_char *
pr_cdname(cp, msg, name, namelen)
	u_char *cp, *msg;
	char *name;
        int namelen;
{
	int n;

	if ((n = dn_expand(msg, msg + 512, cp, name, namelen - 2)) < 0)
		return (NULL);
	if (name[0] == '\0') {
		name[0] = '.';
		name[1] = '\0';
	}
	return (cp + n);
}

const char * resultcodes[] = {
	"NOERROR",
	"FORMERR",
	"SERVFAIL",
	"NXDOMAIN",
	"NOTIMP",
	"REFUSED",
	"6",
	"7",
	"8",
	"9",
	"10",
	"11",
	"12",
	"13",
	"14",
	"NOCHANGE",
};





int
ListHosts(namePtr, queryType, hl)
    char *namePtr;
    int  queryType;  /* e.g. T_A */
    struct hostlist_t * hl;
    
{
	querybuf 		buf, answer;
	struct sockaddr_in 	sin;
	HEADER 			*headerPtr;

	int 			msglen;
	int 			amtToRead;
	int 			numRead;
	int 			i;
	int 			numAnswers = 0;
	int 			result=0;
	int 			soacnt = 0;
	u_short 		len;
	int			dlen;
	int			type;
	int			nscount;
	u_char 			*cp, *nmp;
	char 			name[NAME_LEN];
	char 			dname[2][NAME_LEN];
	char 			domain[NAME_LEN];
/* names and addresses of name servers to try */
#define NUMNS 8
	char			nsname[NUMNS][NAME_LEN];
	int			nshaveaddr[NUMNS];
#define IPADDRSIZE 4
#define NUMNSADDR 16
	char	 		nsipaddr[NUMNSADDR][IPADDRSIZE];
	int			numns;
	int			numnsaddr;
	int			thisns;
	struct hostent		*hp;
	enum {
	    NO_ERRORS, 
	    ERR_READING_LEN, 
	    ERR_READING_MSG,
	    ERR_PRINTING
	} error = NO_ERRORS;

/*
 * normalize to not have trailing dot.  We do string compares below
 * of info from name server, and it won't have trailing dots.
 */
	i = strlen(namePtr);
	if (namePtr[i-1] == '.')
	  namePtr[i-1] = 0;

	if (server_specified) {
	  bcopy(&_res.nsaddr.sin_addr, nsipaddr[0], IPADDRSIZE);
	  numnsaddr = 1;
	}
	else {

/*
 * First we have to find out where to look.  This needs a NS query,
 * possibly followed by looking up addresses for some of the names.
 */

	msglen = res_mkquery(QUERY, namePtr, C_IN, T_NS, NULL,
			     0, NULL, buf.qb2, sizeof buf);

	if (msglen < 0) {
		return (ERROR);
	}

	msglen = res_send(buf.qb2, msglen, answer.qb2, sizeof answer);
	
	if (msglen < 0) {
#ifdef DEBUG
		fprintf(stderr, "Unable to get to nameserver -- try again later\n");
#endif
		return (ERROR);
	}
	

/*
 * Analyze response to our NS lookup
 */

	nscount = ntohs(answer.qb1.ancount) + ntohs(answer.qb1.nscount) +
		  ntohs(answer.qb1.arcount);

	if (answer.qb1.rcode != NOERROR || nscount == 0) {
		switch (answer.qb1.rcode) {
			case NXDOMAIN:
#ifdef DEBUG
				/* Check if it's an authoritive answer */
				if (answer.qb1.aa) {

					fprintf(stderr, "No such domain\n");
				} else {
					fprintf(stderr, "Unable to get information about domain -- try again later.\n");
				}
#endif
				break;
			case SERVFAIL:
#ifdef DEBUG
				fprintf(stderr, "Unable to get information about that domain -- try again later.\n");
#endif
				break;
			case NOERROR:
#ifdef DEBUG
				fprintf(stderr, "That domain exists, but seems to be a leaf node.\n");
#endif
				break;
			case FORMERR:
			case NOTIMP:
			case REFUSED:
#ifdef DEBUG
				fprintf(stderr, "Unrecoverable error looking up domain name.\n");
#endif
				break;
		}
		return (0);
	}

	cp = (u_char *)answer.qb2 + HFIXEDSZ;
	if (ntohs(answer.qb1.qdcount) > 0)
	  cp += dn_skipname(cp, answer.qb2 + msglen) + QFIXEDSZ;

	numns = 0;
	numnsaddr = 0;

/*
 * Look at response from NS lookup for NS and A records.
 */

	for (;nscount; nscount--) {
	  cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp,
			  domain, sizeof(domain));
	  type = _getshort(cp);
	  cp += INT16SZ + INT16SZ + INT32SZ;
	  dlen = _getshort(cp);
	  cp += INT16SZ;
	  if (type == T_NS) {
	    if (dn_expand(answer.qb2, answer.qb2 + msglen, cp, 
			  name, sizeof(name)) >= 0) {
	      if (numns < NUMNS && strcasecmp((char *)domain, namePtr) == 0) {
		for (i = 0; i < numns; i++)
		  if (strcasecmp(nsname[i], (char *)name) == 0)
		    break;  /* duplicate */
		if (i >= numns) {
		  strncpy(nsname[numns], (char *)name, sizeof(name));
		  nshaveaddr[numns] = 0;
 		  numns++;
		}
	      }
	    }
	  }
	  else if (type == T_A) {
	    if (numnsaddr < NUMNSADDR)
	      for (i = 0; i < numns; i++) {
		if (strcasecmp(nsname[i], (char *)domain) == 0) {
		  nshaveaddr[i]++;
		  bcopy(cp, nsipaddr[numnsaddr],IPADDRSIZE);
		  numnsaddr++;
		  break;
		}
	      }
	  }
	  cp += dlen;
	}

/*
 * Usually we'll get addresses for all the servers in the additional
 * info section.  But in case we don't, look up their addresses.
 */

	for (i = 0; i < numns; i++) {
	  if (! nshaveaddr[i]) {
	    register long **hptr;
	    int numaddrs = 0;
#ifndef USE_PTHREADS
	    hp = gethostbyname(nsname[i]);
#else
	    struct hostent * ent;
            char * buffer = malloc(4096);
            int h_err;
            
            hp = NULL;
            ent = malloc(sizeof(struct hostent));
            bzero(ent, sizeof(struct hostent));
            bzero(buffer, 4096);
            gethostbyname_r(nsname[i], ent, buffer,4096, &hp, &h_err);
#endif
	    if (hp) {
	      for (hptr = (long **)hp->h_addr_list; *hptr; hptr++)
		if (numnsaddr < NUMNSADDR) {
		  bcopy((char *)*hptr, nsipaddr[numnsaddr],IPADDRSIZE);
		  numnsaddr++;
		  numaddrs++;
		}
#ifdef USE_PTHREADS
	    free(ent);
            free(buffer);
#endif
	    }
	  
	  }
	}
        }
/*
 * Now nsipaddr has numnsaddr addresses for name servers that
 * serve the requested domain.  Now try to find one that will
 * accept a zone transfer.
 */

	thisns = 0;

again:

	numAnswers = 0;
	soacnt = 0;

	/*
	 *  Create a query packet for the requested domain name.
	 *
	 */
	msglen = res_mkquery(QUERY, namePtr, getclass, T_AXFR, NULL,
			     0, NULL, buf.qb2, sizeof buf);
	if (msglen < 0) {
	    if (_res.options & RES_DEBUG) {
#ifdef DEBUG	   
		fprintf(stderr, "ListHosts: Res_mkquery failed\n");
#endif
	    }
	    return (ERROR);
	}

	bzero((char *)&sin, sizeof(sin));
	sin.sin_family	= AF_INET;
	sin.sin_port	=  htons(NAMESERVER_PORT);

	/*
	 *  Set up a virtual circuit to the server.
	 */

	for (;thisns < numnsaddr; thisns++) {
	  if ((sockFD = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	    perror("ListHosts");
	    return(ERROR);
	  }
	  bcopy(nsipaddr[thisns], &sin.sin_addr, IPADDRSIZE);
	
	  if (connect(sockFD, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
	    break;
	  if (0)
	    perror("Connection failed, trying next server");
	  (void) close(sockFD);
	  sockFD = -1;
	}	
	if (thisns >= numnsaddr) {
#ifdef DEBUG
	  fprintf(stderr, "No server for that domain responded\n");
#endif
	  return (ERROR);
	}

	/*
	 * Send length & message for zone transfer 
	 */

	putshort(msglen, (u_char *)&len);

        if (write(sockFD, (char *)&len, INT16SZ) != INT16SZ ||
            write(sockFD, (char *) &buf, msglen) != msglen) {
		perror("ListHosts");
		(void) close(sockFD);
		sockFD = -1;
		return (ERROR);
	}

	filePtr = stdout;

	while (1) {

	    /*
	     * Read the length of the response.
	     */

	    cp = (u_char *) &buf;
	    amtToRead = INT16SZ;
	    while(amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0){
		cp 	  += numRead;
		amtToRead -= numRead;
	    }
	    if (numRead <= 0) {
		error = ERR_READING_LEN;
		break;
	    }	

	    if ((len = _getshort((u_char*)&buf)) == 0) {
		break;	/* nothing left to read */
	    }

	    /*
	     * Read the response.
	     */

	    amtToRead = len;
	    cp = (u_char *) &buf;
	    while(amtToRead > 0 && (numRead = read(sockFD, cp, amtToRead)) > 0){
		cp += numRead;
		amtToRead -= numRead;
	    }
	    if (numRead <= 0) {
		error = ERR_READING_MSG;
		break;
	    }

	    i = buf.qb1.rcode;
	    if (i != NOERROR || ntohs(buf.qb1.ancount) == 0) {
	      if ((thisns+1) < numnsaddr &&
		  (i == SERVFAIL || i == NOTIMP || i == REFUSED)) {
		
		(void) close(sockFD);
		sockFD = -1;
		thisns++;
		goto again;
	      }
#ifdef DEBUG
	      fprintf(stderr, "Server failed: %s\n",
		     i != NOERROR ? DecodeError(i) : "Premature end of data");
#endif
	      break;
	    }


	    result = printinfo(&buf, cp, queryType, 1);
	    if (! result) {
		error = ERR_PRINTING;
		break;
	    }
	    numAnswers++;
	    cp = buf.qb2 + HFIXEDSZ;
	    if (ntohs(buf.qb1.qdcount) > 0)
		cp += dn_skipname(cp, buf.qb2 + len) + QFIXEDSZ;

	    nmp = cp;
	    cp += dn_skipname(cp, (u_char *)&buf + len);
	    if ((_getshort(cp) == T_SOA)) {
		(void) dn_expand(buf.qb2, buf.qb2 + len, nmp,
				 dname[soacnt], sizeof dname[0]);
	        if (soacnt) {
		    if (strcmp((char *)dname[0], (char *)dname[1]) == 0)
			break;
		} else
		    soacnt++;
	    }
        }

	(void) close(sockFD);
	sockFD = -1;
	switch (error) {
	    case NO_ERRORS:
		return (SUCCESS);

	    case ERR_READING_LEN:
		return(ERROR);

	    case ERR_PRINTING:
#ifdef DEBUG	    
		fprintf(stderr, "*** Error during listing of %s: %s\n", 
				namePtr, DecodeError(result));
#endif
		return(result);

	    case ERR_READING_MSG:
		headerPtr = (HEADER *) &buf;
#ifdef DEBUG
		fprintf(stderr,
	       " ListHosts: error receiving zone transfer: result: %s, answers = %d, authority = %d, additional = %d\n", 
		    	resultcodes[headerPtr->rcode], 
		    	ntohs(headerPtr->ancount), ntohs(headerPtr->nscount), 
			ntohs(headerPtr->arcount));
#endif
		return(ERROR);
	    default:
		return(ERROR);
	}
}

const char *
DecodeError(result)
    int result;
{
	switch(result) {
	    case NOERROR: 	return("Success"); break;
	    case FORMERR:	return("Format error"); break;
	    case SERVFAIL:	return("Server failed"); break;
	    case NXDOMAIN:	return("Non-existent domain"); break;
	    case NOTIMP:	return("Not implemented"); break;
	    case REFUSED:	return("Query refused"); break;
#ifdef NOCHANGE
	    case NOCHANGE:	return("No change"); break;
#endif
	    case NO_INFO: 	return("No information"); break;
	    case ERROR: 	return("Unspecified error"); break;
	    case TIME_OUT: 	return("Timed out"); break;
	    case NONAUTH: 	return("Non-authoritative answer"); break;
	    default: 		break;
	}
	return("BAD ERROR VALUE"); 
}

#endif
