/* * sma -- Sendmail log analyser * * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``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 JARKKO TURKULAINEN 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. * * $Date: 2002/12/06 12:48:51 $ */ /* Copyright for routines in scan_time() */ /* * Copyright (c) 1985, 1987, 1988, 1993 * 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: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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 REGENTS 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. */ #include "sma.h" #ifndef __DragonFly__ int isdigit(int); #endif /* * Take month as an ascii string and return integer */ int conv_mon(const char *mon) { int k; const char *montab[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; for (k = 0; k < 12; k++) { if (!strcmp(mon, montab[k])) break; } return k; } /* * Take month, day, hour, minute and second as integers * and return time_t */ time_t conv_time(int mon, int day, int hh, int min, int sec) { /* * Assume that the year is current year... * This is bug. */ /* sanity check: */ if (mon < 0 || mon >= 12) return (time_t)NULL; /* fill the structure: */ tp.tm_sec=sec; tp.tm_min=min; tp.tm_hour=hh; tp.tm_mday=day; tp.tm_mon=mon; tp.tm_year=curr->tm_year; tp.tm_isdst=-1; return mktime(&tp); } void scan_time(time_t *utime, char *p) { #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; struct tm *lt; char *t, *dot; int bigyear; int yearset = 0; for (t = p, dot = NULL; *t; ++t) { if (isdigit(*t)) continue; if (*t == '.' && dot == NULL) { dot = t; continue; } usage(); } tval = time((time_t *)NULL); lt = localtime(&tval); lt->tm_isdst = -1; /* correct for DST */ if (dot != NULL) { /* .SS */ *dot++ = '\0'; if (strlen(dot) != 2) usage(); lt->tm_sec = ATOI2(dot); if (lt->tm_sec > 61) usage(); } else lt->tm_sec = 0; switch (strlen(p)) { case 12: /* cc */ bigyear = ATOI2(p); lt->tm_year = bigyear * 100 - 1900; yearset = 1; /* FALLTHROUGH */ case 10: /* yy */ if (yearset) { lt->tm_year += ATOI2(p); } else { lt->tm_year = ATOI2(p); if (lt->tm_year < 69) /* hack for 2000 ;-} */ lt->tm_year += (2000 - 1900); else lt->tm_year += (1900 - 1900); } /* FALLTHROUGH */ case 8: /* mm */ lt->tm_mon = ATOI2(p); if ((lt->tm_mon > 12) || !lt->tm_mon) usage(); --lt->tm_mon; /* time struct is 0 - 11 */ /* FALLTHROUGH */ case 6: /* dd */ lt->tm_mday = ATOI2(p); if ((lt->tm_mday > 31) || !lt->tm_mday) usage(); /* FALLTHROUGH */ case 4: /* HH */ lt->tm_hour = ATOI2(p); if (lt->tm_hour > 23) usage(); /* FALLTHROUGH */ case 2: /* MM */ lt->tm_min = ATOI2(p); if (lt->tm_min > 59) usage(); break; default: usage(); } *utime = mktime(lt); } /* * Prepare a space for sorted address stuctures and do the sorting */ void sort(struct host *hptr) { int i, j, k; struct envpair *eptr; struct in *iptr; struct out *optr; struct relpair *rptr; struct rin *riptr; struct rout *roptr; struct status *statptr; struct rule *ruleptr; struct rrelay *rrptr; /* space for sorted structures: */ if (!(hptr->setab = malloc(hptr->edif * sizeof(struct envpair *)))) error_memory(); if (!(hptr->sitab = malloc(hptr->idif * sizeof(struct sin *)))) error_memory(); if (!(hptr->sotab = malloc(hptr->odif * sizeof(struct sout *)))) error_memory(); if (!(hptr->srtab = malloc(hptr->rrdif * sizeof(struct relpair *)))) error_memory(); if (!(hptr->rsitab = malloc(hptr->ridif * sizeof(struct rin *)))) error_memory(); if (!(hptr->rsotab = malloc(hptr->rodif * sizeof(struct rout *)))) error_memory(); if (!(hptr->ssttab = malloc(hptr->sdif * sizeof(struct status *)))) error_memory(); if (!(hptr->sruletab = malloc(hptr->rdif * sizeof(struct rule *)))) error_memory(); /* envelope pairs: */ for (i = 0, k = 0; k < asize; k++) for (eptr = hptr->etab[k]; eptr; eptr = eptr->next) /* copy pointers: */ hptr->setab[i++] = eptr; /* relay pairs: */ for (i = 0, k = 0; k < rsize; k++) for (rptr = hptr->rtab[k]; rptr; rptr = rptr->next) /* copy pointers: */ hptr->srtab[i++] = rptr; /* input: */ for (i = 0, k = 0; k < asize; k++) for (iptr = hptr->itab[k]; iptr; iptr = iptr->next) /* copy pointers: */ hptr->sitab[i++] = iptr; for (i = 0, k = 0; k < rsize; k++) for (riptr = hptr->ritab[k]; riptr; riptr = riptr->next) /* copy pointers: */ hptr->rsitab[i++] = riptr; /* output: */ for (i = 0, k = 0; k < asize; k++) for (optr = hptr->otab[k]; optr; optr = optr->next) /* copy pointers: */ hptr->sotab[i++] = optr; for (i = 0, k = 0; k < rsize; k++) for (roptr = hptr->rotab[k];roptr; roptr = roptr->next) /* copy pointers: */ hptr->rsotab[i++] = roptr; /* status and ruleset: */ for (i = 0, k = 0; k < rsize; k++) for (statptr = hptr->sttab[k]; statptr; statptr = statptr->next) /* copy pointers: */ hptr->ssttab[i++] = statptr; for (i = 0, k = 0; k < rsize; k++) for (ruleptr = hptr->ruletab[k]; ruleptr; ruleptr = ruleptr->next) { /* copy pointers: */ hptr->sruletab[i++] = ruleptr; /* alloc space for sorted relays: */ if (!(ruleptr->srrelaytab = malloc(ruleptr->reldif * sizeof(struct rrelay *)))) error_memory(); for (j = 0, rrptr = ruleptr->rrelaytab; rrptr; rrptr = rrptr->next) ruleptr->srrelaytab[j++] = rrptr; /* sort */ qsort(ruleptr->srrelaytab, ruleptr->reldif, sizeof(struct rrelay *), comp_rrel); } /* sort */ qsort(hptr->setab, hptr->edif, sizeof(struct envpair *), comp_env); qsort(hptr->sitab, hptr->idif, sizeof(struct in *), comp_in); qsort(hptr->sotab, hptr->odif, sizeof(struct out *), comp_out); qsort(hptr->srtab, hptr->rrdif, sizeof(struct relpair *), comp_rel); qsort(hptr->rsitab, hptr->ridif, sizeof(struct rin *), comp_rin); qsort(hptr->rsotab, hptr->rodif, sizeof(struct rout *), comp_rout); qsort(hptr->ssttab, hptr->sdif, sizeof(struct status *), comp_s); qsort(hptr->sruletab, hptr->rdif, sizeof(struct rule *), comp_r); } /* * Comparing functions, called from sort() */ int comp_env(const void *p1, const void *p2) { const struct envpair * sp1 = *(const struct envpair * const *)p1; const struct envpair * sp2 = *(const struct envpair * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_rel(const void *p1, const void *p2) { const struct relpair * sp1 = *(const struct relpair * const *)p1; const struct relpair * sp2 = *(const struct relpair * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_in(const void *p1, const void *p2) { const struct in * sp1 = *(const struct in * const *)p1; const struct in * sp2 = *(const struct in * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_rin(const void *p1, const void *p2) { const struct rin * sp1 = *(const struct rin * const *)p1; const struct rin * sp2 = *(const struct rin * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_out(const void *p1, const void *p2) { const struct out * sp1 = *(const struct out * const *)p1; const struct out * sp2 = *(const struct out * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_rout(const void *p1, const void *p2) { const struct rout * sp1 = *(const struct rout * const *)p1; const struct rout * sp2 = *(const struct rout * const *)p2; if (sflag) { if (sp1->size < sp2->size) return (1); if (sp1->size > sp2->size) return (-1); } else { if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); } return (0); } int comp_s(const void *p1, const void *p2) { const struct status * sp1 = *(const struct status * const *)p1; const struct status * sp2 = *(const struct status * const *)p2; if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); return (0); } int comp_r(const void *p1, const void *p2) { const struct rule * sp1 = *(const struct rule * const *)p1; const struct rule * sp2 = *(const struct rule * const *)p2; if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); return (0); } int comp_rrel(const void *p1, const void *p2) { const struct rrelay * sp1 = *(const struct rrelay * const *)p1; const struct rrelay * sp2 = *(const struct rrelay * const *)p2; if (sp1->num < sp2->num) return (1); if (sp1->num > sp2->num) return (-1); return (0); } /* * Calculate the average daily/hour messages */ void average(struct host *hptr, int *dd, float *ad, int *hh, float *ah) { int fulld; int fullh; int weeks; int hours; int hfday, hfhour; int i; hfday = hptr->fday; hfhour = hptr->fhour; /* integer value of hours: */ fullh = (int)hptr->dtime/3600; fullh++; /* integer value of days: */ fulld = (int)hptr->dtime/(24*3600); fulld++; /* * calculate full weeks: * * days left over from last full week are returned * as int fulld: */ weeks = 0; for (; fulld >= 7; fulld -= 7) weeks++; /* calculate daily averages: */ for (i = 0; i < 7; i++) if (i == hfday && fulld > 0) { ad[i] = (float)dd[i] / ((float)weeks + 1.0); fulld--; hfday++; } else if (weeks) ad[i] = (float)dd[i] / (float)weeks; else ad[i] = (float)dd[i]; /* same calculations for hours: */ hours = 0; for (; fullh >= 24; fullh -= 24) hours++; for (i = 0; i < 24; i++) if (i == hfhour && fullh > 0) { ah[i] = (float)hh[i] / ((float)hours + 1.0); fullh--; hfhour++; } else if (hours) ah[i] = (float)hh[i] / (float)hours; else ah[i] = (float)hh[i]; } /* Strip newline: */ char * stripn(char *s) { char *p; for (p = s; *s != '\n'; s++) ; *s = '\0'; return p; } void error_memory() { fprintf(stderr, "%s: memory allocation failure, errno: %d\n", pname, errno); exit(1); } void copying() { fprintf(stderr, "\nSMA version %s\n\n" , VERSION); fprintf(stderr, "This program is Copyright (c) 2000 - 2003\n" " Jarkko Turkulainen. All rights reserved.\n" "Some parts are " "Copyright (c) 1992, 1993, 1994\n" " Henry Spencer. All rights reserved.\n" "Some parts are " "Copyright (c) 1985, 1987, 1988, 1993, 1994\n" " The Regents of the University of California. " "All rights reserved.\n\n"); exit(0); } void usage() { /* Use stdout - easier to "more" or "less" .. */ fprintf(stdout, "\nSMA version %s\n\n" , VERSION); fprintf(stdout, "usage: %s [OPTIONS] [files...]\n" "OPTIONS are\n" " -A\t\tforce addresses to lower case to help with consistent counts\n" " -a\t\tset the output format as ascii\n" " -b \tbackground color (RGB) of the HTML form\n" " -c\t\tshow copyright information and exit\n" " -C \tset report header as \n" " -D \tanalyse only log entries between dates d1 and d2\n" " -d\t\tprint only the domain portion of email addresses\n" " -f \tuse configuration from instead of default\n" " -F\t\tdo not use default configuration file even if it exists\n" " -h\t\tprint this text and exit\n" " -H \toverride the hostname with \n" " -i\t\tprint ASCII report as HTML comment (requires -w or -O html)\n" " -L \tprocess only lines with syslog tag \n" " -n\t\tdo not print the time distribution\n" " -o \twrite output to \n" " -O \tset output format (ascii/html/clog)\n" " -p\t\tprint current configuration to stdout\n" " -s\t\tsort by tranfers\n" " -q\t\tbe quiet (no error messages)\n" " -v\t\tshow debugging information\n" " -l \tprint top addresses\n" " -r \tprint top relay addresses\n" " -t \tset the internal hash table size (normal/big/huge/a,r)\n" " -w\t\tset the output format as html\n" " files\tlog files\n\n", pname); exit(0); } void dump_config(FILE *handle) { char *p = NULL; char dates[128]; fprintf(handle, " BgColor %s\n", bchar); fprintf(handle, " BounceAddress %s\n", bechar); fprintf(handle, " CaseSensitive %s\n", csflag ? "no" : "yes"); if (cfchar) fprintf(handle, " ClogFormat \"%s\"\n", cfchar); fprintf(handle, " ClogSentOnly %s\n", clsflag ? "yes" : "no"); fprintf(handle, " Comment \"%s\"\n", Cchar); fprintf(handle, " Debug %s\n", vflag ? "yes" : "no"); if (eetime) { strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&eetime)); fprintf(handle, " EndTime %s\n", dates); } fprintf(handle, " EnvelopePairs %d\n", epnum); fprintf(handle, " EnvelopeRecipientFilter %s\n", ref ? ref : "*"); fprintf(handle, " EnvelopeRecipients %d\n", lrnum); fprintf(handle, " EnvelopeSenderFilter %s\n", sef ? sef : "*"); fprintf(handle, " EnvelopeSenders %d\n", lnum); if (ftchar) fprintf(handle, " FooterText \"%s\"\n", ftchar); switch(Oflag) { case FORMAT_ASCII: if (!(p = strdup("ascii"))) error_memory(); break; case FORMAT_HTML: if (!(p = strdup("html"))) error_memory(); break; case FORMAT_CLOG: if (!(p = strdup("clog"))) error_memory(); break; } fprintf(handle, " Format %s\n", p); free(p); fprintf(handle, " HashTables %d,%d\n", asize, rsize); if (htchar) fprintf(handle, " HeaderText \"%s\"\n", htchar); if (Hchar) fprintf(handle, " HostName \"%s\"\n", Hchar); fprintf(handle, " IncludeAscii %s\n", iflag ? "yes" : "no"); if (ochar) fprintf(handle, " OutFile \"%s\"\n", ochar); if (plchar) fprintf(handle, " PictureLink \"%s\"\n", plchar); if (ppchar) fprintf(handle, " PictureParameters \"%s\"\n", ppchar); if (pachar) fprintf(handle, " PictureURL %s\n", pachar); fprintf(handle, " PrintGeneralInfo %s\n", pgflag ? "yes" : "no"); fprintf(handle, " PrintRuleset %s\n", rsnum ? "yes" : "no"); fprintf(handle, " PrintStatus %s\n", stnum ? "yes" : "no"); fprintf(handle, " RelayPairs %d\n", rpnum); fprintf(handle, " RelayRecipientFilter %s\n", rrf ? rrf : "*"); fprintf(handle, " RelayRecipients %d\n", rrnum); fprintf(handle, " RelaySenderFilter %s\n", srf ? srf : "*"); fprintf(handle, " RelaySenders %d\n", rnum); fprintf(handle, " RulesetRelays %d\n", rsrnum); fprintf(handle, " ShowUsers %s\n", dflag ? "no" : "yes"); fprintf(handle, " Silent %s\n", qflag ? "yes" : "no"); fprintf(handle, " Sorting %s\n", sflag ? "transfer" : "number"); if (sstime) { strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&sstime)); fprintf(handle, " StartTime %s\n", dates); } if (Lchar) fprintf(handle, " SyslogTag %s\n", Lchar); fprintf(handle, " TbColor %s\n", tbchar); }