/*
 * Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000
 *	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.
 */
#ifndef lint
static const char rcsid[] =
    "@(#) $Id: ec.c,v 1.28 2000/10/13 22:49:15 leres Exp $ (LBL)";
#endif

/*
 * ec - manufactures ethernet code routines
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>

#if __STDC__
struct mbuf;
struct rtentry;
#endif
#include <net/if.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <ctype.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "gnuc.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif

#include "arpwatch.h"
#include "ec.h"
#include "util.h"

/* Basic data structure */
struct ecent {
	u_int32_t o;		/* first 3 octets */
	char *text;		/* associated text */
};

static struct ecent *list;
static u_int ec_last = 0;
static u_int ec_len = 0;

/* Forwards */
static int ec_a2o(char *, u_int32_t *);

/* Convert an 3 octets from an ethernet address to a u_int32_t */
static int
ec_a2o(register char *cp, register u_int32_t *op)
{
	char xbuf[128];
	u_char e[6];

	(void)sprintf(xbuf, "%.32s:0:0:0", cp);
	if (!str2e(xbuf, e))
		return (0);
	*op = 0;
	BCOPY(e, op, 3);
	return (1);
}

/* Add a ethernet code to the database */
int
ec_add(register u_int32_t o, register char *text)
{

	if (ec_last >= ec_len) {
		if (list == NULL) {
			ec_len = 512;
			list = malloc(ec_len * sizeof(*list));
		} else {
			ec_len *= 2;
			list = realloc(list, ec_len * sizeof(*list));
		}
		if (list == NULL) {
			syslog(LOG_ERR, "ec_add(): malloc: %m");
			exit(1);
		}
	}
	list[ec_last].o = o;
	list[ec_last].text = savestr(text);
	++ec_last;
	return (1);
}

/* Find the manufacture for a given ethernet address */
char *
ec_find(register u_char *e)
{
	u_int32_t o;
	register int i;

	o = 0;
	BCOPY(e, &o, 3);
	for (i = 0; i < ec_last; ++i)
		if (list[i].o == o)
			return (list[i].text);

	return (NULL);
}

/* Loop through the ethernet code database */
int
ec_loop(register FILE *f, ec_process fn, register const char *nm)
{
	register int n;
	register char *cp, *cp2, *text;
	register int sawblank;
	u_int32_t o;
	char line[1024];

	n = 0;
	while (fgets(line, sizeof(line), f)) {
		++n;
		cp = line;
		cp2 = cp + strlen(cp) - 1;
		if (cp2 >= cp && *cp2 == '\n')
			*cp2++ = '\0';
		if (*cp == '#')
			continue;
		if ((cp2 = strchr(cp, '\t')) == 0) {
			syslog(LOG_ERR, "ec_loop(): %s:%d missing tab", nm, n);
			continue;
		}

		/* 3 octets come first */
		*cp2++ = '\0';
		text = cp2;
		if (!ec_a2o(cp, &o)) {
			syslog(LOG_ERR, "ec_loop(): %s:%d bad octets", nm, n);
			continue;
		}

		/* Compress blanks */
		cp = cp2 = text;
		sawblank = 0;
		while (*cp != '\0') {
			if (sawblank) {
				*cp2++ = ' ';
				sawblank = 0;
			}
			*cp2++ = *cp++;
			while (isspace((int)*cp)) {
				++cp;
				sawblank = 1;
			}
		}
		*cp2 = '\0';

		if (!(*fn)(o, text))
			return (0);
	}

	return (1);
}

/* DECnet local logical address prefix */
static u_char decnet[3] = { 0xaa, 0x0, 0x4 };

/* Returns true if an ethernet address is decnet, else false */
int
isdecnet(register u_char *e)
{

	return (MEMCMP(e, decnet, sizeof(decnet)) == 0);
}

/* Convert an ascii ethernet string to ethernet address */
int
str2e(register char *str, register u_char *e)
{
	register int i;
	u_int n[6];

	MEMSET(n, 0, sizeof(n));
	if (sscanf(str, "%x:%x:%x:%x:%x:%x",
	    &n[0], &n[1], &n[2], &n[3], &n[4], &n[5]) != 6)
		return (0);
	for (i = 0; i < 6; ++i) {
		if (n[i] > 0xff)
			return (0);
		e[i] = n[i];
	}
	return (1);
}

/* Convert an ethernet address to an ascii ethernet string */
char *
e2str(register u_char *e)
{
	static char str[32];

	(void)sprintf(str, "%x:%x:%x:%x:%x:%x",
	    e[0], e[1], e[2], e[3], e[4], e[5]);
	return (str);
}
