/*
 * Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2009, 2010, 2013, 2016, 2019, 2023
 * 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 the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#ifndef lint
static const char rcsid[] __attribute__((unused)) =
    "@(#) $Id: ec.c 1537 2023-09-05 17:50:56Z leres $ (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>
#include <errno.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;
static u_int ec_len;

/* 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(char *cp, u_int32_t *op)
{
	char xbuf[128];
	u_char e[6];

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

/* Add a ethernet code to the database */
int
ec_add(u_int32_t o, const char *text)
{
	struct ecent *olist;

	if (ec_last >= ec_len) {
		if (list == NULL) {
			ec_len = 512;
			olist = NULL;
			list = malloc(ec_len * sizeof(*list));
		} else {
			ec_len *= 2;
			olist = list;
			list = realloc(olist, ec_len * sizeof(*list));
		}
		if (list == NULL) {
			if (olist != NULL)
				free(olist);
			lg(LOG_ERR, "ec_add(): malloc: %s", strerror(errno));
			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(const u_char *e)
{
	u_int32_t o;
	int i;

	o = 0;
	memmove(&o, e, 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(FILE *f, ec_process fn, const char *nm)
{
	int n;
	char *cp, *cp2, *text;
	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) {
			lg(LOG_ERR, "ec_loop(): %s:%d missing tab", nm, n);
			continue;
		}

		/* 3 octets come first */
		*cp2++ = '\0';
		text = cp2;
		if (!ec_a2o(cp, &o)) {
			lg(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(const u_char *e)
{
	return (memcmp(e, decnet, sizeof(decnet)) == 0);
}

/* Convert an ascii ethernet string to ethernet address */
int
str2e(const char *str, u_char *e)
{
	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(const u_char *e)
{
	static char str[32];

	(void)snprintf(str, sizeof(str),
	    zeropad ? "%02x:%02x:%02x:%02x:%02x:%02x" : "%x:%x:%x:%x:%x:%x",
	    e[0], e[1], e[2], e[3], e[4], e[5]);
	return (str);
}
