/* * Copyright (c) 1990, 1991, 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: report.c,v 1.46 2000/09/30 23:41:04 leres Exp $ (LBL)"; #endif
/* * report - arpwatch report generating routines */
#include <sys/param.h> #include <sys/types.h> /* concession to AIX */ #include <sys/socket.h> #include <sys/time.h> #include <sys/wait.h>
#if struct mbuf; struct rtentry; #endif #include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #ifdef TIME_WITH_SYS_TIME #include <time.h> #endif #include <unistd.h>
#include "gnuc.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif
#include "addresses.h" #include "arpwatch.h" #include "dns.h" #include "ec.h" #include "report.h" #include "setsignal.h" #include "util.h"
#define PLURAL(n) ((n) == 1 || (n) == -1 ? "" : "s")
static int cdepth; /* number of outstanding children */
static char *fmtdate(time_t); static char *fmtdelta(time_t); RETSIGTYPE reaper(int); static int32_t gmt2local(void);
static char * fmtdelta(register time_t t) { register char *cp; register int minus; static char buf[132];
minus = 0; if (t < 0) { t = -t; ++minus; } if (t < 60) { cp = "second"; } else if (t < 60 * 60) { t /= 60; cp = "minute"; } else if (t < 24 * 60 * 60) { t /= (60 * 60); cp = "hour"; } else { t /= (24 * 60 * 60); cp = "day"; } if (minus) t = -t; (void)sprintf(buf, "%u %s%s", (u_int32_t)t, cp, PLURAL(t)); return(buf); }
static char *dow[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static char *moy[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
#define DOW(d) ((d) < 0 || (d) >= 7 ? "?" : dow[d]) #define MOY(m) ((m) < 0 || (m) >= 12 ? "?" : moy[(m)])
static char * fmtdate(time_t t) { register struct tm *tm; register int32_t mw; register char ch; static int init = 0; static char zone[32], buf[132];
if (t == 0) return("<no date>");
if (!init) { mw = gmt2local() / 60; if (mw < 0) { ch = '-'; mw = -mw; } else { ch = '+'; } (void)sprintf(zone, "%c%02d%02d", ch, mw / 60, mw % 60); }
tm = localtime(&t); (void)sprintf(buf, "%s, %s %d, %d %d:%02d:%02d %s", DOW(tm->tm_wday), MOY(tm->tm_mon), tm->tm_mday, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, zone); return(buf); }
/* * Returns the difference between gmt and local time in seconds. * Use gmtime() and localtime() to keep things simple. */ static int32_t gmt2local(void) { register int dt, dir; register struct tm *gmt, *loc; time_t t; struct tm sgmt;
t = time(NULL); gmt = &sgmt; *gmt = *gmtime(&t); loc = localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60;
/* * If the year or julian day is different, we span 00:00 GMT * and must add or subtract a day. Check the year first to * avoid problems when the julian day wraps. */ dir = loc->tm_year - gmt->tm_year; if (dir == 0) dir = loc->tm_yday - gmt->tm_yday; dt += dir * 24 * 60 * 60;
return (dt); }
RETSIGTYPE reaper(int signo) { register pid_t pid; DECLWAITSTATUS status;
for (;;) { pid = waitpid((pid_t)0, &status, WNOHANG); if ((int)pid < 0) { /* ptrace foo */ if (errno == EINTR) continue; /* ECHILD means no one left */ if (errno != ECHILD) syslog(LOG_ERR, "reaper: %m"); break; } /* Already got everyone who was done */ if (pid == 0) break; --cdepth; if (WEXITSTATUS(status)) syslog(LOG_DEBUG, "reaper: pid %d, exit status %d", pid, WEXITSTATUS(status)); } return RETSIGVAL; }
void report(register char *title, register u_int32_t a, register u_char *e1, register u_char *e2, register time_t *t1p, register time_t *t2p) { register char *cp, *hn; register int fd, pid; register FILE *f; char tempfile[64], cpu[64], os[64]; char *fmt = "%20s: %s\n"; char *watcher = WATCHER; char *watchee = WATCHEE; char *sendmail = PATH_SENDMAIL; char *unknown = "<unknown>"; char buf[132]; static int init = 0;
/* No report until we're initialized */ if (initializing) return;
if (debug) { if (debug > 1) { dosyslog(LOG_NOTICE, title, a, e1, e2); return; } f = stdout; (void)putc('\n', f); } else { /* Setup child reaper if we haven't already */ if (!init) { (void)setsignal(SIGCHLD, reaper); ++init; } while (cdepth >= 3) { syslog(LOG_ERR, "report: pausing (cdepth %d)", cdepth); pause(); }
/* Syslog this event too */ dosyslog(LOG_NOTICE, title, a, e1, e2);
/* Update child depth */ ++cdepth;
/* Fork off child to send mail */ pid = fork(); if (pid) { /* Parent */ if (pid < 0) syslog(LOG_ERR, "report: fork() 1: %m"); return; }
/* Child */ closelog(); (void)strcpy(tempfile, "/tmp/arpwatch.XXXXXX"); if ((fd = mkstemp(tempfile)) < 0) { syslog(LOG_ERR, "mkstemp(%s) %m", tempfile); exit(1); } if ((f = fdopen(fd, "w+")) == NULL) { syslog(LOG_ERR, "child fdopen(%s): %m", tempfile); exit(1); } /* Cheap delete-on-close */ if (unlink(tempfile) < 0) syslog(LOG_ERR, "unlink(%s): %m", tempfile); }
(void)fprintf(f, "From: %s\n", watchee); (void)fprintf(f, "To: %s\n", watcher); hn = gethname(a); if (!isdigit(*hn)) (void)fprintf(f, "Subject: %s (%s)\n", title, hn); else { (void)fprintf(f, "Subject: %s\n", title); hn = unknown; } (void)putc('\n', f); (void)fprintf(f, fmt, "hostname", hn); (void)fprintf(f, fmt, "ip address", intoa(a)); (void)fprintf(f, fmt, "ethernet address", e2str(e1)); if ((cp = ec_find(e1)) == NULL) cp = unknown; (void)fprintf(f, fmt, "ethernet vendor", cp); if (hn != unknown && gethinfo(hn, cpu, sizeof(cpu), os, sizeof(os))) { (void)sprintf(buf, "%s %s", cpu, os); (void)fprintf(f, fmt, "dns cpu & os", buf); } if (e2) { (void)fprintf(f, fmt, "old ethernet address", e2str(e2)); if ((cp = ec_find(e2)) == NULL) cp = unknown; (void)fprintf(f, fmt, "old ethernet vendor", cp); } if (t1p) (void)fprintf(f, fmt, "timestamp", fmtdate(*t1p)); if (t2p) (void)fprintf(f, fmt, "previous timestamp", fmtdate(*t2p)); if (t1p && t2p && *t1p && *t2p) (void)fprintf(f, fmt, "delta", fmtdelta(*t1p - *t2p));
if (debug) { fflush(f); return; }
(void)rewind(f); if (dup2(fileno(f), fileno(stdin)) < 0) { syslog(LOG_ERR, "dup2: %m"); exit(1); } /* XXX Need to freopen()? */ /* Always Deliver interactively (pause when child depth gets large) */ execl(sendmail, "sendmail", "-odi", watcher, NULL); syslog(LOG_ERR, "execl: %s: %m", sendmail); exit(1); }
|