/*
 * Copyright (c) 1996, 1997, 1999, 2004, 2009, 2010, 2012, 2013, 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 copyright[] __attribute__((unused)) =
    "Copyright (c) 1996, 1997, 1999, 2004, 2009, 2010, 2012, 2013, 2019, 2023\n\
The Regents of the University of California.  All rights reserved.\n";
static const char rcsid[] __attribute__((unused)) =
    "@(#) $Id: arpsnmp.c 1537 2023-09-05 17:50:56Z leres $ (LBL)";
#endif

/*
 * arpsnmp - keep track of ethernet/ip address pairings, report changes
 */

#include <sys/param.h>
#include <sys/types.h>				/* concession to AIX */
#include <sys/file.h>

#include <ctype.h>
#include <errno.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

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

#include "arpwatch.h"
#include "db.h"
#include "ec.h"
#include "file.h"
#include "machdep.h"
#include "util.h"

/* Forwards */
int	main(int, char **);
int	readsnmp(const char *);
int	snmp_add(u_int32_t, const u_char *, time_t, const char *);
void	usage(void) __attribute__((noreturn));

/* Globals */
char *prog;
int quiet;
int suppress;
const char *watcher = WATCHER;
const char *watchee = WATCHEE;

extern int optind;
extern int opterr;
extern char *optarg;

int
main(int argc, char **argv)
{
	char *cp;
	int op, i;
	char errbuf[256];

	if ((cp = strrchr(argv[0], '/')) != NULL)
		prog = cp + 1;
	else
		prog = argv[0];

	if (abort_on_misalignment(errbuf) < 0) {
		(void)fprintf(stderr, "%s: %s\n", prog, errbuf);
		exit(1);
	}

	opterr = 0;
	while ((op = getopt(argc, argv, "CdD:f:qsw:W:Z")) != EOF)
		switch (op) {

		case 'C':
			zeropad = 0;
			break;

		case 'd':
			++debug;
#ifndef DEBUG
			(void)fprintf(stderr,
			    "%s: Warning: Not compiled with -DDEBUG\n", prog);
#endif
			break;

		case 'D':
			arpdir = optarg;
			break;

		case 'f':
			arpfile = optarg;
			break;

		case 'q':
			++quiet;
			break;

		case 's':
			++suppress;
			break;

		case 'w':
			watcher = optarg;
			break;

		case 'W':
			watchee = optarg;
			break;

		case 'Z':
			zeropad = 1;
			break;

		default:
			usage();
		}
	
	if (optind == argc)
		usage();

	openlog(prog, 0, LOG_DAEMON);

	/* Read in database */
	initializing = 1;
	/* XXX todo: file locking */
	if (!readdata())
		exit(1);
	sorteinfo();
#ifdef DEBUG
	if (debug > 2) {
		debugdump();
		exit(0);
	}
#endif
	initializing = 0;

	/* Suck files in then exit */
	for (i = optind; i < argc; ++i)
		(void)readsnmp(argv[i]);
	if (!dump())
		exit(1);
	exit(0);
}

static time_t now;

int
snmp_add(u_int32_t a, const u_char *e, time_t t, const char *h)
{
	/* Watch for ethernet broadcast */
	if (memcmp(e, zero, 6) == 0 || memcmp(e, allones, 6) == 0) {
		if (!quiet)
			dosyslog(LOG_INFO, "ethernet broadcast", a, e, NULL);
		return (1);
	}

	/* Watch for some ip broadcast addresses */
	if (a == 0 || a == 1) {
		if (!quiet)
			dosyslog(LOG_INFO, "ip broadcast", a, e, NULL);
		return (1);
	}

	/* Use current time if none found in file */
	if (t == 0)
		t = now;

	return (ent_add(a, e, t, h));
}

/* Process an snmp file */
int
readsnmp(const char *file)
{
	FILE *f;

	if (debug > 2)
		(void)fprintf(stderr, "%s: reading %s\n", prog, file);
	if ((f = fopen(file, "r")) == NULL) {
		syslog(LOG_ERR, "fopen(%s): %s", file, strerror(errno));
		return(0);
	}
	now = time(NULL);
	if (!file_loop(f, snmp_add, file)) {
		(void)fclose(f);
		return(0);
	}
	(void)fclose(f);
	return(1);
}

void
lg(int pri, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	(void)fprintf(stderr, "%s: ", prog);
	(void)vfprintf(stderr, fmt, ap);
	(void)fputc('\n', stderr);
	va_end(ap);
}

void
usage(void)
{
	extern char version[];

	(void)fprintf(stderr, "Version %s\n", version);
	(void)fprintf(stderr,
	    "usage: %s [-CdqsZ] [-D arpdir] [-f datafile] [-w watcher@email]\n"
	    "\t[-W watchee@email] file ...\n", prog);
	exit(1);
}
