/*
 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
 *      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
 * 
 *-----------------------------------------------------------------------------
 *
 * Modifed from tcpdump's addrtoname.c by Mike Borella for ipgrab
 *
 * $Log: addrtoname.c,v $
 * Revision 1.2  1998/06/12 21:00:56  mborella
 * Added log tag
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include "config.h"

#define HASHNAMESIZE 4096

struct hnamemem 
{
  u_int addr;
  char *name;
  struct hnamemem *nxt;
};

struct enamemem 
{
  u_short e_addr0;
  u_short e_addr1;
  u_short e_addr2;
  char *e_name;
  u_char *e_nsap;                 /* used only for nsaptable[] */
  struct enamemem *e_nxt;
};

/*
 * From libpcap
 */

extern struct eproto 
{
  char *s;
  u_short p;
} eproto_db[];

struct hnamemem tporttable[HASHNAMESIZE];
struct hnamemem uporttable[HASHNAMESIZE];
struct hnamemem eprototable[HASHNAMESIZE];
struct enamemem enametable[HASHNAMESIZE];

static char hex[] = "0123456789abcdef";

/*----------------------------------------------------------------------------
**
** newhnamemem()
**
** Return a zero'ed hnamemem struct and cuts down on calloc() overhead 
**
**----------------------------------------------------------------------------
*/

struct hnamemem *newhnamemem(void)
{
  struct hnamemem *p;
  static struct hnamemem *ptr = NULL;
  static u_int num = 0;
  void ftlerr(char *, ...);

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


/*----------------------------------------------------------------------------
**
** init_servarray()
**
** Set up array with service to port mappings for both TCP and UDP
**
**----------------------------------------------------------------------------
*/

void init_servarray(void)
{
  struct servent *sv;
  struct hnamemem *table;
  int i;
  
  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;

      table->name = strdup(sv->s_name);
      
      table->addr = port;
      table->nxt = newhnamemem();
    }
  endservent();
}


/*----------------------------------------------------------------------------
**
** udpport_string()
**
** Translate a well-known UDP port number to its service name. 
**
**----------------------------------------------------------------------------
*/

char *udpport_string(u_short port)
{
  struct hnamemem *tp;
  u_int 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();

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


/*----------------------------------------------------------------------------
**
** tcpport_string()
**
** Translate a well-known TCP port number to its service name. 
**
**----------------------------------------------------------------------------
*/

char *tcpport_string(u_short port)
{
  struct hnamemem *tp;
  u_int 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();
  
  sprintf(buf, "%u", i);
  tp->name = strdup(buf);
  return (tp->name);
}


/*----------------------------------------------------------------------------
**
** lookup_emem()
**
** Find the hash node that corresponds the ether address 'ep'
**
**----------------------------------------------------------------------------
*/

struct enamemem *lookup_emem(const u_char *ep)
{
  u_int i, j, k;
  struct enamemem *tp;
  void ftlerr(char *,...);

  k = (ep[0] << 8) | ep[1];
  j = (ep[2] << 8) | ep[3];
  i = (ep[4] << 8) | ep[5];
  
  tp = &enametable[(i ^ j) & (HASHNAMESIZE-1)];
  while (tp->e_nxt)
    {
      if (tp->e_addr0 == i && tp->e_addr1 == j && tp->e_addr2 == k)
	return tp;
      else
	tp = tp->e_nxt;
    }
  tp->e_addr0 = i;
  tp->e_addr1 = j;
  tp->e_addr2 = k;
  tp->e_nxt = (struct enamemem *) calloc (1, sizeof(*tp));
  if (tp->e_nxt == NULL) ftlerr("lookup_emem: calloc");

  return tp;
}

/*----------------------------------------------------------------------------
**
** init_eprotoarray()
**
** Initialize the Ethernet protocol table
**
**----------------------------------------------------------------------------
*/

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

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

/*----------------------------------------------------------------------------
**
** etheraddr_string()
**
** Translate an ethernet address to a colon-delimited string
**
**----------------------------------------------------------------------------
*/

char *etheraddr_string(u_char *ep)
{
  u_int i, j;
  char *cp;
  struct enamemem *tp;
  char buf[sizeof("00:00:00:00:00:00")];

  tp = lookup_emem(ep);
  if (tp->e_name) return (tp->e_name);

  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';
  tp->e_name = strdup(buf);
  return (tp->e_name);
}


/*----------------------------------------------------------------------------
**
** etherproto_string()
**
** Translate an ethernet protocol id to a network-layer protocol string
**
**----------------------------------------------------------------------------
*/

char *etherproto_string(u_short port)
{
  char *cp;
  struct hnamemem *tp;
  u_int i = port;
  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;
  port = ntohs(port);
  *cp++ = hex[port >> 12 & 0xf];
  *cp++ = hex[port >> 8 & 0xf];
  *cp++ = hex[port >> 4 & 0xf];
  *cp++ = hex[port & 0xf];
  *cp++ = '\0';
  tp->name = strdup(buf);
  return (tp->name);
}


/*----------------------------------------------------------------------------
**
** init_addrtoname()
**
** Initialize all of the tables
**
**----------------------------------------------------------------------------
*/

void init_addrtoname(void)
{
  init_servarray();
  init_eprotoarray();
}
