/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)gthostnamadr.c	1.11	94/05/03 SMI"	/* SVr4.0 1.5	*/

/*
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * 		PROPRIETARY NOTICE (Combined)
 * 
 * This source code is unpublished proprietary information
 * constituting, or derived under license from AT&T's UNIX(r) System V.
 * In addition, portions of such source code were derived from Berkeley
 * 4.3 BSD under license from the Regents of the University of
 * California.
 * 
 * 
 * 
 * 		Copyright Notice 
 * 
 * Notice of copyright on this source code product does not indicate 
 * publication.
 * 
 * 	(c) 1986,1987,1988.1989  Sun Microsystems, Inc
 * 	(c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
 * 	          All rights reserved.
 *  
 */


#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <ctype.h>
#include <sys/tiuser.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netconfig.h>
#include <netdir.h>
#include <string.h>

int h_errno;

/*
 * Internet version.
 */
static struct hostdata {
#define MAXALIASES 20
	char	*host_aliases[MAXALIASES];
#define MAXADDRS    10
#define HOSTADDRSIZE    4   /* assumed == sizeof u_long */
	char    hostaddr[MAXADDRS][HOSTADDRSIZE];
	char    *addr_list[MAXADDRS+1];
	struct	sockaddr_in nbuf;
	struct	hostent host;
	char	hostname[MAXHOSTNAMELEN];
} *hostdata, *_hostdata();

extern struct netconfig *__rpc_getconfip();
static int err_conv(); /* converts _nderror from netdir to h_errno */

/*
 * Internal routine to allocate hostdata on heap, instead of
 * putting lots of stuff into the data segment.
 */
static struct hostdata *
_hostdata()
{
	register struct hostdata *d = hostdata;

	if (d == 0) {
		d = (struct hostdata *)calloc(1, sizeof (struct hostdata));
		hostdata = d;
	}
	return(d);
}
	
/*
 * gethostby*() routines have been written in terms of netdir_getby*() to
 * have a uniform policy for getting hostnames. Now, this creates a weird
 * situation, that set/gethostent() routines still refer only to the local
 * database. Things may change drastically in the future.
 */
struct hostent *
gethostbyname(nam)
	register char *nam;
{
	register struct hostdata *d = _hostdata();
	struct nd_hostserv service;
	struct netconfig *nconf;
	struct nd_addrlist *addrs;
	struct netbuf *na;
	struct sockaddr_in *sa;
	int count, i, neterr;
	
	if (d == 0)
		return ((struct hostent*)NULL);
	service.h_host = nam;
	service.h_serv = NULL; /* netdir_getbyname of tcpip optimized for this case */
	if ((nconf = __rpc_getconfip("udp")) == NULL &&
	    (nconf = __rpc_getconfip("tcp")) == NULL)
		return((struct hostent *)NULL);
	else if ((neterr = netdir_getbyname(nconf, &service, &addrs)) != 0) {
#ifdef DEBUG
		fprintf(stderr, "gethostbyname: %s\n", netdir_sperror());
#endif
		(void) freenetconfigent(nconf);
		h_errno = err_conv(neterr);
		return((struct hostent *)NULL);
	}	
	(void) freenetconfigent(nconf);

	/*
	 * build up list of all adresses in hostent form.
	 * This is INTERNET SPECIFIC.
	 */

	count = addrs->n_cnt;
	d->host.h_aliases = d->host_aliases;
	d->addr_list[0] = d->hostaddr[0];
	d->host.h_addr_list = d->addr_list;	
	for (na = addrs->n_addrs,i=0;count && na && i < MAXADDRS;count--, na++) {
		sa = (struct sockaddr_in *)na->buf;
		d->addr_list[i] = d->hostaddr[i];
		*((u_long *)d->hostaddr[i++]) = (u_long)sa->sin_addr.s_addr;
	}
	d->addr_list[i] = NULL;
	d->host.h_name = nam;
	d->host.h_addrtype = AF_INET;
	d->host.h_length = HOSTADDRSIZE;
	*(d->host.h_aliases) = NULL; /* we can't figure out aliases here */
	netdir_free((char *)addrs, ND_ADDRLIST);
	return (&d->host);
}

struct hostent *
gethostbyaddr(addr, length, type)
	char *addr;
	register int length;
	register int type;
{
	register struct hostdata *d = _hostdata();
	struct netconfig *nconf;
	struct nd_hostservlist *addrs;
	struct nd_hostserv *hs;
	struct netbuf nbuf;
	struct sockaddr_in sa;
	struct t_info tinfo;
	int	fd, neterr;
	
	if (d == 0)
		return((struct hostent *)NULL);
	sa.sin_addr.s_addr = *(u_long *)addr;
	sa.sin_family = AF_INET;
	sa.sin_port = 0; /* netdir_getbyaddr of tcpip optimized for this case */
	sa.sin_zero[0] = '\0';
	nbuf.buf = (char *)&d->nbuf;
	memmove(nbuf.buf, (char *)&sa, sizeof(sa));
	if ((nconf = __rpc_getconfip("udp")) == NULL &&
		(nconf = __rpc_getconfip("tcp")) == NULL)
		return((struct hostent *)NULL);
	else {
		if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) {
			(void) freenetconfigent(nconf);
			return((struct hostent *)NULL);
		}
		(void) t_close(fd);
		nbuf.maxlen = tinfo.addr; 
		nbuf.len = nbuf.maxlen; /* ??? */
		if ((neterr = netdir_getbyaddr(nconf, &addrs, &nbuf)) != 0) {
#ifdef DEBUG
			fprintf(stderr, "gethostbyaddr: %s\n", netdir_sperror());
#endif
			(void) freenetconfigent(nconf);
			h_errno = err_conv(neterr);
			return((struct hostent *)NULL);
		}	
	}
	(void) freenetconfigent(nconf);

	hs = addrs->h_hostservs;
	d->host.h_aliases = d->host_aliases;
	d->host.h_name = hs->h_host;		
	(void) memcpy(d->hostname, hs->h_host, sizeof(d->hostname));
	d->host.h_name = d->hostname;		
	d->addr_list[0] = d->hostaddr[0];
	d->host.h_addr_list = d->addr_list;
	memmove(d->addr_list[0], addr, length);
	d->host.h_addrtype = AF_INET;
	d->host.h_length = length;
	*(d->host.h_aliases) = NULL; /* we can't figure out aliases here */
	/*
	 * can't figure out how many hostname aliases, it's a
	 * complex operation of finding out distinct hostnames
     * from a matrix of addrs->h_cnt (i.e. hosts*serves) pairs.
   	 */
	netdir_free((char *)addrs, ND_HOSTSERVLIST);
	return(&d->host);
}

static int
err_conv(nerr)
int nerr;
{
	switch (nerr) {
	case ND_TRY_AGAIN:
		return(TRY_AGAIN);
	case ND_NO_RECOVERY:
		return(NO_RECOVERY);
	case ND_NO_DATA:
		return(NO_DATA);
	case ND_NOHOST:
		return(HOST_NOT_FOUND);
	default:
		return(0);
	}
}  
