#ifndef lint
static char *RCSid = "$Header: /proj/freeware1.0/nfswatch/nfswatch4.1/RCS/netaddr.c,v 1.1 1995/08/08 04:40:30 rck Exp $";
#endif

#include "os.h"

/*
 * netaddr.c - routines for working with network addresses.
 *
 * David A. Curry				Jeffrey C. Mogul
 * Purdue University				Digital Equipment Corporation
 * Engineering Computer Network			Western Research Laboratory
 * 1285 Electrical Engineering Building		250 University Avenue
 * West Lafayette, IN 47907-1285		Palo Alto, CA 94301
 * davy@ecn.purdue.edu				mogul@decwrl.dec.com
 *
 * $Log: netaddr.c,v $
 * Revision 1.1  1995/08/08  04:40:30  rck
 * initial checkin
 *
 * Revision 4.3  93/10/11  15:36:33  mogul
 * Bugfix for SUNOS5
 * 
 * Revision 4.2  1993/10/04  18:31:07  mogul
 * Added code to discover if a given IP address is local to this host.
 *
 * Revision 4.1  93/09/28  21:24:42  mogul
 * Explicit data type for IP addresses.
 * 
 * Revision 4.0  93/03/01  19:59:00  davy
 * NFSWATCH Version 4.0.
 * 
 * Revision 3.4  1993/02/24  17:44:45  davy
 * Added -auth mode, changes to -proc mode, -map option, -server option.
 *
 * Revision 3.3  1993/01/16  19:08:59  davy
 * Corrected Jeff's address.
 *
 * Revision 3.2  1993/01/15  19:33:39  davy
 * Miscellaneous cleanups.
 *
 * Revision 3.1  1993/01/13  20:18:17  davy
 * Put in OS-specific define scheme, and merged in Tim Hudson's code for
 * SGI systems (as yet untested).
 *
 * Revision 3.0  1991/01/23  08:23:06  davy
 * NFSWATCH Version 3.0.
 *
 * Revision 1.2  90/08/17  15:47:24  davy
 * NFSWATCH Version 2.0.
 * 
 * Revision 1.1  88/11/29  11:20:35  davy
 * NFSWATCH Release 1.0
 * 
 */
#include <sys/param.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#ifdef	SUNOS5
#include <sys/sockio.h>
#endif	/* SUNOS5 */
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include "nfswatch.h"
#include "externs.h"

#define	MAXOURADDRS 32
ipaddrt	ouraddrs[MAXOURADDRS];

/*
 * get_net_addrs - get network addresses of source and destination
 *		   hosts, along with official host names.
 */
void
get_net_addrs()
{
	register int n;
	char *inet_ntoa();
	register char **cp;
	struct hostent *hp;

	/*
	 * Look up the local host.
	 */
	if ((hp = gethostbyname(myhost)) == NULL) {
		(void) fprintf(stderr, "%s: %s: unknown host.\n", pname,
			myhost);
		finish(-1);
	}

	setup_ouraddrs();

	/*
	 * Save the official host name.
	 */
	(void) strcpy(myhost, hp->h_name);

	/*
	 * If one was specified, look up the destination host.
	 * Otherwise, we can use what we have.
	 */
	if (allflag) {
		(void) sprintf(dsthost, "all hosts");
	}
	else if (dstflag) {
		if ((hp = gethostbyname(dsthost)) == NULL) {
			(void) fprintf(stderr, "%s: %s: unknown host.\n", pname,
				dsthost);
			finish(-1);
		}

		/*
		 * Save the official host name.
		 */
		(void) strcpy(dsthost, hp->h_name);
	}
	else {
		/*
		 * Host name is the same as the local
		 * host.
		 */
		(void) strcpy(dsthost, myhost);
	}

	/*
	 * Copy destination host's network addresses.
	 */
	n = 0;
	(void) bzero((char *) dstaddrs, MAXHOSTADDR * sizeof(ipaddrt));

	for (cp = hp->h_addr_list; *cp != NULL; cp++) {
		if (n >= MAXHOSTADDR)
			break;

		(void) bcopy(*cp, (char *) &dstaddrs[n], hp->h_length);
		n++;
	}

	/*
	 * If they specified a server host, get its addresses.
	 */
	if (serverflag) {
		if ((hp = gethostbyname(serverhost)) == NULL) {
			fprintf(stderr, "%s: %s: unknown host.\n", pname,
				serverhost);
			finish(-1);
		}

		/*
		 * Save the official host name.
		 */
		(void) strcpy(serverhost, hp->h_name);

		/*
		 * Copy the server's network addresses.
		 */
		n = 0;
		(void) bzero((char *) serveraddrs, MAXHOSTADDR *
			     sizeof(ipaddrt));

		for (cp = hp->h_addr_list; *cp != NULL; cp++) {
			if (n >= MAXHOSTADDR)
				break;

			(void) bcopy(*cp, (char *) &serveraddrs[n],
				     hp->h_length);
			n++;
		}
	}

	/*
	 * If they didn't specify a source host,
	 * we're done.
	 */
	if (!srcflag)
		return;

	/*
	 * Look up the source host.
	 */
	if ((hp = gethostbyname(srchost)) == NULL) {
		(void) fprintf(stderr, "%s: %s: unknown host.\n", pname,
			srchost);
		finish(-1);
	}

	/*
	 * Save the official host name.
	 */
	(void) strcpy(srchost, hp->h_name);

	/*
	 * Copy source host's network addresses.
	 */
	n = 0;
	(void) bzero((char *) srcaddrs, MAXHOSTADDR * sizeof(ipaddrt));

	for (cp = hp->h_addr_list; *cp != NULL; cp++) {
		if (n >= MAXHOSTADDR)
			break;

		(void) bcopy(*cp, (char *) &srcaddrs[n], hp->h_length);
		n++;
	}
}

/*
 * want_packet - determine if we're interested in a packet by examining
 *		 its source and destination addresses.
 */
int
want_packet(src, dst)
ipaddrt src, dst;
{
	register int i, want;

	want = FALSE;
	thisdst = dst;

	/*
	 * Check that the source or destination is the server.
	 */
	if (serverflag) {
		for (i=0; (serveraddrs[i] != 0) && (i < MAXHOSTADDR); i++) {
			if (!bcmp((char *) &src, (char *) &serveraddrs[i],
				  sizeof(ipaddrt)) ||
			    !bcmp((char *) &dst, (char *) &serveraddrs[i],
				  sizeof(ipaddrt))) {
				want = TRUE;
				break;
			}
		}
		return(want);
	}
	 
	/*
	 * Any source or destination is okay.
	 */
	if (allflag) {
		return(TRUE);
	}

	/*
	 * Check source address first.
	 */
	if (srcflag) {
		for (i = 0; (srcaddrs[i] != 0) && (i < MAXHOSTADDR); i++) {
			if (!bcmp((char *) &src, (char *) &srcaddrs[i],
			    sizeof(ipaddrt))) {
				want = TRUE;
				break;
			}
		}

		/*
		 * If it's not from our source, we
		 * don't even need to check the destination.
		 */
		if (!want)
			return(FALSE);
	}

	want = FALSE;

	/*
	 * Check destination address.
	 */
	for (i = 0; (dstaddrs[i] != 0) && (i < MAXHOSTADDR); i++) {
		if (!bcmp((char *) &dst, (char *) &dstaddrs[i],
		    sizeof(ipaddrt))) {
			want = TRUE;
			break;
		}
	}

	return(want);
}

/*
 * to_self - determine if packet destination is the local host
 */
int
to_self(dst)
ipaddrt dst;
{
	register int i;

	/*
	 * Check if the destination is one of our addresses
	 */
	for (i=0; (ouraddrs[i] != 0) && (i < MAXOURADDRS); i++) {
	    if (ouraddrs[i] == dst) {
		return(1);
	    }
	}
	
	return(0);
}

/* Not all systems have IFF_LOOPBACK */
#ifdef IFF_LOOPBACK
#define ISLOOPBACK(p) ((p)->ifr_flags & IFF_LOOPBACK)
#else
#define ISLOOPBACK(p) (strcmp((p)->ifr_name, "lo0") == 0)
#endif

/*
 * Make a list of our possible IP addresses
 * A lot of the code in setup_ouraddrs() was borrowed from tcpdump/
 */
setup_ouraddrs()
{
	struct ifreq ibuf[MAXOURADDRS], *ifrp, *ifend;
	struct ifconf ifc;
	int n;
	int fd;

	n = 0;
	(void) bzero((char *) ouraddrs, MAXOURADDRS * sizeof(ipaddrt));

	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
	    perror("socket");
	    finish(-1);
	}
	ifc.ifc_len = sizeof ibuf;
	ifc.ifc_buf = (caddr_t)ibuf;

	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
			ifc.ifc_len < sizeof(struct ifreq)) {
	    perror("SIOCGIFCONF");
	    finish(-1);
	}
	ifrp = ibuf;
	ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
	
	while (ifrp < ifend) {
		struct ifreq ifr;
		struct sockaddr_in *sin =
				(struct sockaddr_in *)&ifrp->ifr_addr;
		/*
		 * Need a temporary to preserve address info that is
		 * used below to locate the next entry.  (Otherwise,
		 * SIOCGIFFLAGS stomps over it because the requests
		 * are returned in a union.)
		 */
		bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
		if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
			(void)fprintf(stderr, "SIOCGIFFLAGS: ");
			perror(ifrp->ifr_name);
			finish(-1);
		}
		if ((ifr.ifr_flags & IFF_UP) && !ISLOOPBACK(&ifr)) {
		    if (n >= MAXOURADDRS)
			break;
		    ouraddrs[n] = sin->sin_addr.s_addr;
		    n++;
		}
#if BSD >= 199006
		n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
		if (n < sizeof(*ifrp))
			++ifrp;
		else
			ifrp = (struct ifreq *)((char *)ifrp + n);
#else
		++ifrp;
#endif
	}
	close(fd);
}
