/*
 *	Copyright (c) 1993-1997 JSC Rinet, Novosibirsk, Russia
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that this entire comment appears intact.
 * Redistribution in binary form may occur without any restrictions.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
 */

/* addrtoname.c -- address to name translation routines */

/* Portions of this software may fall under the following copyrights: */
/*
 * Copyright (c) 1988, 1990 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: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *  Internet, ethernet, port, and protocol string to address
 *  and address to string conversion routines
 */

#ifdef	HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#ifdef	TIME_WITH_SYS_TIME
#include <time.h>
#endif

#include "trafshow.h"

typedef void (*sigfunc) ();

/* Hash tables for whatever-to-name translations */

#define HASHNAMESIZE		4096

/* IP level */
struct hnamemem {
	u_int32_t addr;
	char *name;
	struct hnamemem *nxt;
};
struct hnamemem hnametable[HASHNAMESIZE];
struct hnamemem tporttable[HASHNAMESIZE];
struct hnamemem uporttable[HASHNAMESIZE];

/* Ethernet level */
struct enamemem {
	u_short addr0;
	u_short addr1;
	u_short addr2;
	char *name;
	struct enamemem *nxt;
};
struct enamemem enametable[HASHNAMESIZE];
struct hnamemem eprototable[HASHNAMESIZE];


static u_int32_t f_netmask;
static u_int32_t f_localnet;
static u_int32_t netmask;

/*
 * A faster replacement for inet_ntoa().
 */
char *
intoa(addr)
	u_int32_t addr;
{
	register char *cp;
	register u_int byte;
	register int n;
	static char buf[sizeof(".xxx.xxx.xxx.xxx")];

	addr = ntohl(addr);
	cp = &buf[sizeof buf];
	*--cp = '\0';

	n = 4;
	do {
		byte = addr & 0xff;
		*--cp = byte % 10 + '0';
		byte /= 10;
		if (byte > 0) {
			*--cp = byte % 10 + '0';
			byte /= 10;
			if (byte > 0)
				*--cp = byte + '0';
		}
		*--cp = '.';
		addr >>= 8;
	} while (--n > 0);

	return cp + 1;
}

/* Return a zero'ed hnamemem struct and cuts down on calloc() overhead */
struct hnamemem *
newhnamemem()
{
	register struct hnamemem *p;
	static struct hnamemem *ptr = NULL;
	static u_int num = 0;

	if (num <= 0) {
		num = 64;
		ptr = (struct hnamemem *)calloc(num, sizeof (*ptr));
		if (ptr == NULL) error(1, "newhnamemem: calloc");
	}
	--num;
	p = ptr++;
	return (p);
}

/* A replacement for strdup() that cuts down on malloc() overhead */
char *
savestr(str)
	register const char *str;
{
	register u_int size;
	register char *p;
	static char *strptr = NULL;
	static u_int strsize = 0;

	size = strlen(str) + 1;
	if (size > strsize) {
		strsize = 1024;
		if (strsize < size)
			strsize = size;
		strptr = (char *)malloc(strsize);
		if (strptr == NULL) error(1, "savestr: malloc");
	}
	(void)strcpy(strptr, str);
	p = strptr;
	strptr += size;
	strsize -= size;
	return (p);
}

/*
 * "getname" is written in this atrocious way to make sure we don't
 * wait forever while trying to get hostnames from yp.
 */

#ifdef	HAVE_SIGINTERRUPT	/* BSD signal semantics */
#include <setjmp.h>

jmp_buf getname_env;

static void
nohostname()
{
	longjmp(getname_env, 1);
}
#else	/* SYSV */

static void
nohostname()
{
	/* empty */
}
#endif

/*
 * Return a name for the IP address pointed to by ap.  This address
 * is assumed to be in network byte order.
 */
char *
getname(ap)
	u_char *ap;
{
	register char *cp;
	u_int32_t addr;
		/* static for longjmp */
	static struct hostent *hp;
	static struct hnamemem *p;
	static int oldtimer;
	static sigfunc oldalarm;

#ifndef LBL_ALIGN
	addr = *(const u_int32_t *)ap;
#else
	/*
	 * Extract 32 bits in network order, dealing with alignment.
	 */
	switch ((long)ap & 3) {
	case 0:
		addr = *(u_int32_t *)ap;
		break;
	case 2:
#ifdef WORDS_BIGENDIAN
		addr = ((u_int32_t)*(u_short *)ap << 16) |
			(u_int32_t)*(u_short *)(ap + 2);
#else
		addr = ((u_int32_t)*(u_short *)(ap + 2) << 16) |
			(u_int32_t)*(u_short *)ap;
#endif
		break;
	default:
#ifdef WORDS_BIGENDIAN
		addr = ((u_int32_t)ap[0] << 24) |
			((u_int32_t)ap[1] << 16) |
			((u_int32_t)ap[2] << 8) |
			(u_int32_t)ap[3];
#else
		addr = ((u_int32_t)ap[3] << 24) |
			((u_int32_t)ap[2] << 16) |
			((u_int32_t)ap[1] << 8) |
			(u_int32_t)ap[0];
#endif
		break;
	}
#endif
	for (p = &hnametable[addr & (HASHNAMESIZE-1)]; p->nxt; p = p->nxt)
		if (p->addr == addr)
			return (p->name);

	p->addr = addr;
	p->nxt = newhnamemem();
	hp = NULL;

	/*
	 * Only print names when:
	 * 	(1) -n was not given.
	 *	(2) Address is foreign and -f was given.  If -f was not 
	 *	    present, f_netmask and f_local are 0 and the second
	 *	    test will succeed.
	 *	(3) The host portion is not 0 (i.e., a network address).
	 *	(4) The host portion is not broadcast.
	 */
	if (!nflag && (addr & f_netmask) == f_localnet &&
	    (addr &~ netmask) != 0 && (addr | netmask) != 0xffffffff) {
		oldtimer = alarm(dns_timeout);
		oldalarm = signal(SIGALRM, nohostname);
#ifdef	HAVE_SIGINTERRUPT
		if (!setjmp(getname_env))
#endif
			hp = gethostbyaddr((char *)&addr, 4, AF_INET);
		(void)signal(SIGALRM, oldalarm);
		if (oldtimer < 1) oldtimer = 1;
		(void)alarm(oldtimer);
	}
	if (hp) {
		if (Nflag && (cp = strchr(hp->h_name, '.')) != NULL)
			*cp = '\0';
		cp = hp->h_name;
	} else	cp = intoa(addr);

	p->name = savestr(cp);
	return p->name;
}

char *
tcpport_string(port)
	u_short port;
{
	register struct hnamemem *tp;
	register u_int32_t i = port;
	char buf[sizeof("00000")];

	for (tp = &tporttable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
		if (tp->addr == i)
			return (tp->name);

	tp->addr = i;
	tp->nxt = newhnamemem();

	(void)sprintf(buf, "%u", i);
	tp->name = savestr(buf);
	return (tp->name);
}

char *
udpport_string(port)
	u_short port;
{
	register struct hnamemem *tp;
	register u_int32_t i = port;
	char buf[sizeof("00000")];

	for (tp = &uporttable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
		if (tp->addr == i)
			return (tp->name);

	tp->addr = i;
	tp->nxt = newhnamemem();

	(void)sprintf(buf, "%u", i);
	tp->name = savestr(buf);
	return (tp->name);
}

static void
init_servarray()
{
	struct servent *sv;
	register struct hnamemem *table;
	register int i;
	char buf[sizeof("0000000000")];

	while ((sv = getservent()) != NULL) {
		int port = ntohs(sv->s_port);
		i = port & (HASHNAMESIZE-1);
		if (strcmp(sv->s_proto, "tcp") == 0)
			table = &tporttable[i];
		else if (strcmp(sv->s_proto, "udp") == 0)
			table = &uporttable[i];
		else continue;

		while (table->name) table = table->nxt;

		if (nflag) {
			(void)sprintf(buf, "%d", port);
			table->name = savestr(buf);
		} else	table->name = savestr(sv->s_name);
		table->addr = port;
		table->nxt = newhnamemem();
	}
	endservent();
}

static char hex[] = "0123456789abcdef";

char *
entoa(ep)
	register u_char *ep;
{
	register u_int i, j;
	register char *cp;
	static char buf[sizeof("00:00:00:00:00:00")];

	cp = buf;
	if ((j = *ep >> 4) != 0)
		*cp++ = hex[j];
	*cp++ = hex[*ep++ & 0xf];
	for (i = 5; (int)--i >= 0;) {
		*cp++ = ':';
		if ((j = *ep >> 4) != 0)
			*cp++ = hex[j];
		*cp++ = hex[*ep++ & 0xf];
	}
	*cp = '\0';

	return buf;
}

char *
etheraddr_string(ep)
	register u_char *ep;
{
	register u_int i, j, k;
	register struct enamemem *tp;
	u_char *ap;

	k = (ep[0] << 8) | ep[1];
	j = (ep[2] << 8) | ep[3];
	i = (ep[4] << 8) | ep[5];

	for (tp = &enametable[(i ^ j) & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
		if (tp->addr0 == i && tp->addr1 == j && tp->addr2 == k)
			return (tp->name);

	tp->addr0 = i;
	tp->addr1 = j;
	tp->addr2 = k;
	tp->nxt = (struct enamemem *)calloc(1, sizeof(*tp));
	if (tp->nxt == NULL) error(1, "etheraddr_string: calloc");

	if ((ap = getarptab(ep)) != NULL)
		ap = getname(ap);
	else	ap = entoa(ep);

	tp->name = savestr(ap);
	return (tp->name);
}

char *
etherproto_string(proto)
	u_short proto;
{
	register char *cp;
	register struct hnamemem *tp;
	register u_int32_t i = proto;
	char buf[sizeof("0000")];

	for (tp = &eprototable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
		if (tp->addr == i)
			return (tp->name);

	tp->addr = i;
	tp->nxt = newhnamemem();

	cp = buf;
	proto = ntohs(proto);
	*cp++ = hex[proto >> 12 & 0xf];
	*cp++ = hex[proto >> 8 & 0xf];
	*cp++ = hex[proto >> 4 & 0xf];
	*cp++ = hex[proto & 0xf];
	*cp++ = '\0';
	tp->name = savestr(buf);
	return (tp->name);
}

/* XXX from pcap library */
extern struct eproto {
	char *s;
	u_short p;
} eproto_db[];

static void
init_eprotoarray()
{
	register int i;
	register struct hnamemem *tp;

	for (i = 0; eproto_db[i].s; i++) {
		for (tp = &eprototable[ntohs(eproto_db[i].p) & (HASHNAMESIZE-1)];
		     tp->name; tp = tp->nxt) ;
		tp->name = eproto_db[i].s;
		tp->addr = ntohs(eproto_db[i].p);
		tp->nxt = newhnamemem();
	}
}

/*
 * Initialize the address to name translation machinery.  We map all
 * non-local IP addresses to numeric addresses if fflag is true (i.e.,
 * to prevent blocking on the nameserver).  localnet is the IP address
 * of the local network.  mask is its subnet mask.
 */
void
init_addrtoname(localnet, mask)
	u_int32_t localnet;
	u_int32_t mask;
{
	netmask = mask;
	if (fflag) {
		f_localnet = localnet;
		f_netmask = mask;
	}
	if (nflag)
		/*
		 * Simplest way to suppress names.
		 */
		return;

	init_servarray();
	init_eprotoarray();
}


syntax highlighted by Code2HTML, v. 0.9.1