#include #include "buffer.h" #include "case.h" #include "exit.h" #include "fmt.h" #include "getln.h" #include "logmsg.h" #include "open.h" #include "scan.h" #include "str.h" #include "stralloc.h" #include "datetime.h" #include "now.h" #define WHO "qmail-mrtg" #define TAI64NLEN 24 /** @file qmail-mrtg.c @return 0: ok 1: Error: No TAI64N timestamp available 2: Warning: Not enough time left between calls */ /* qmail-send */ int local = 0; int remote = 0; int success = 0; int failure = 0; int bytes = 0; int tlstrans = 0; int deferral = 0; int bounces = 0; int triples = 0; int qmtp = 0; int qmtps = 0; /* qmail-smtpd */ int asessions = 0; int rsessions = 0; int aorig = 0; int arcpt = 0; int rsend = 0; int rhelo = 0; int rorigbad = 0; int rorigdns = 0; int rrcptbad = 0; int rrcptfail = 0; int rsize = 0; int rmime = 0; int rloader = 0; int rvirus = 0; int rspam = 0; int aauth = 0; int rauth = 0; int atls = 0; int rtls = 0; int spfpass = 0; int spfail = 0; /* qmail-pop3d */ int apop = 0; int rpop = 0; int pok = 0; int pdeny = 0; /* *server + rblsmtpd */ int sok = 0; int sdeny = 0; int greet = 0; int grey = 0; int rbl = 0; char bufsmall[64]; buffer bo = BUFFER_INIT(write, 1, bufsmall, sizeof(bufsmall)); static void outs(char *s) { if (buffer_puts(&bo, s) == -1) _exit(1); if (buffer_puts(&bo, "\n") == -1) _exit(1); if (buffer_flush(&bo) == -1) _exit(1); } static void out(int i) { char num[FMT_ULONG]; if (buffer_put(&bo, num, fmt_ulong(num, (unsigned long)i)) == -1) _exit(1); if (buffer_puts(&bo, "\n") == -1) _exit(1); if (buffer_flush(&bo) == -1) _exit(1); } char bufspace[1024]; buffer bi = BUFFER_INIT(read, 0, bufspace, sizeof(bufspace)); static void mrtg_results(char flag) { switch (flag) { case '1': out(success); out(tlstrans); break; case '2': bytes = bytes / 1024; out(bytes); out(bytes); break; case '3': out(local); out(remote); break; case '4': out(failure); out(deferral); break; case '5': out(bounces); out(triples); break; case '6': qmtps += qmtp; out(qmtp); out(qmtps); break; /* QMTP */ case 'a': out(asessions); out(rsessions); break; /* total */ case 'b': out(aorig); out(arcpt); break; /* accepted */ case 'c': out(rsend); out(rhelo); break; /* rejected MTA */ case 'd': out(rorigbad); out(rorigdns); break; /* Orig */ case 'e': out(rrcptbad); out(rrcptfail); break; /* Recipient */ case 'f': out(rmime); out(rloader); break; /* Warlord */ case 'g': out(rvirus); out(rspam); break; /* Infected/Spam */ case 'h': out(aauth); out(rauth); break; /* Auth */ case 'i': out(atls); out(rtls); break; /* TLS */ case 'j': out(spfpass); out(spfail); break; /* SPF */ case 'k': out(grey); break; /* Greylisted */ case 'z': sdeny += rbl; out(sok); out(sdeny); break; /* reject session */ case 'A': out(apop); out(rpop); break; case 'B': out(pok); out(pdeny); break; default: break; } } static void mrtg_sendlog(char *in, char flag) { int i, j, k = 0; switch (flag) { case '1': if (case_starts(in, "delivery")) { i = str_chr(in, ':') + 2; if (case_starts(in + i, "success:")) success++; i = str_chr(in, 'T'); if (case_starts(in + i, "TLS_")) tlstrans++; } break; case '2': if (case_starts(in, "info msg")) { i = str_chr(in, ':') + 8; if ((j = str_chr(in + i, ' '))) in[i + j] = '\0'; bytes += atoi(in + i); } break; case '3': if (case_starts(in, "status:")) { i = str_rchr(in, 'c') + 4; k = str_rchr(in, 'r') + 7; if ((j = str_chr(in + i, '/'))) in[i + j] = '\0'; if (atoi(in + i) > local) local = atoi(in + i); if ((j = str_chr(in + k, '/'))) in[k + j] = '\0'; if (atoi(in + k) > remote) remote = atoi(in + k); } break; case '4': if (case_starts(in, "delivery")) { i = str_chr(in, ':') + 2; if (case_starts(in + i, "failure:")) failure++; if (case_starts(in + i, "deferral:")) deferral++; } break; case '5': if (case_starts(in, "bounce msg")) bounces++; if (case_starts(in, "triple bounce:")) triples++; break; case '6': if (case_starts(in, "delivery")) { i = str_chr(in, 'q'); if (case_starts(in + i, "qmtp:_ok")) qmtp++; if (case_starts(in + i, "qmtps:_ok")) qmtps++; } break; default: break; } } static void mrtg_smtplog(char *in, char flag) { int i, j, k = 0; i = str_chr(in, 'A'); j = str_chr(in, 'R'); k = str_chr(in, 'P'); switch (flag) { case 'a': if (case_starts(in + i, "Accept")) asessions++; if (case_starts(in + j, "Reject")) rsessions++; break; case 'b': if (case_starts(in + i, "Accept::ORIG:")) aorig++; if (case_starts(in + i, "Accept::RCPT:")) arcpt++; break; case 'c': if (case_starts(in + j, "Reject::SNDR::Invalid_Relay")) rsend++; if (case_starts(in + j, "Reject::SNDR::Bad_Helo")) rhelo++; if (case_starts(in + j, "Reject::SNDR::DNS_Helo")) rhelo++; break; case 'd': if (case_starts(in + j, "Reject::ORIG::Bad_Mailfrom")) rorigbad++; if (case_starts(in + j, "Reject::ORIG::DNS_MF")) rorigdns++; break; case 'e': if (case_starts(in + j, "Reject::RCPT::Bad_Rcptto")) rrcptbad++; if (case_starts(in + j, "Reject::RCPT::Failed_Rcptto")) rrcptfail++; break; case 'f': if (case_starts(in + j, "Reject::DATA::Invalid_Size")) rsize++; if (case_starts(in + j, "Reject::DATA::Bad_MIME")) rmime++; if (case_starts(in + j, "Reject::DATA::MIME_Attach")) rmime++; if (case_starts(in + j, "Reject::DATA::Bad_Loader")) rloader++; break; case 'g': if (case_starts(in + j, "Reject::DATA::Spam_Message")) rspam++; if (case_starts(in + j, "Reject::DATA::Virus_Infected")) rvirus++; break; case 'h': if (case_starts(in + i, "Accept::AUTH:")) aauth++; if (case_starts(in + j, "Reject::AUTH:")) rauth++; break; case 'i': if (case_starts(in + k, "P:ESMTPS")) atls++; if (case_starts(in + j, "Reject::TLS:")) rtls++; break; case 'j': if (case_starts(in + i, "Accept::SPF:")) spfpass++; if (case_starts(in + j, "Reject::SPF:")) spfail++; break; case 'k': if (case_starts(in + i, "Deferred::SNDR::Grey_Listed")) grey++; break; case 'z': if (case_starts(in, "tcpserver") || case_starts(in, "sslserver") || case_starts(in, "rblsmtpd")) { i = str_chr(in, ':') + 2; if (case_starts(in + i, "ok")) sok++; if (case_starts(in + i, "deny")) sdeny++; j = str_chr(in + i, ':') + 2; if (case_starts(in + i + j, "451")) rbl++; if (case_starts(in + i + j, "553")) rbl++; if (case_starts(in + i + j, "greetdelay:")) greet++; } break; default: break; } } static void mrtg_pop3log(char *in, char flag) { int i, j = 0; switch (flag) { case 'A': i = str_chr(in, 'A'); j = str_chr(in, 'R'); if (case_starts(in + i, "Accept::AUTH:")) apop++; if (case_starts(in + j, "Reject::AUTH:")) rpop++; break; case 'B': if (case_starts(in, "tcpserver:") || case_starts(in, "sslserver:")) { i = str_chr(in, ':') + 2; if (case_starts(in + i, "ok")) pok++; if (case_starts(in + i, "deny")) pdeny++; } break; default: break; } } int main(int argc, char **argv) { int i; int c; int match; int enoughtime = 0; unsigned long u; unsigned long calltime; unsigned long seconds; unsigned long nanoseconds; unsigned int history = 305; char flag; stralloc line = {0}; calltime = now(); if (argc < 2) logmsg( WHO, 100, USAGE, "qmail-mrtg [ -1 | -2 | -3 | -4 | -5 | -6 | -a | -b | -c | -d | -e | -f " "| -g | -h | -i | -j | -k | -z | -A | -B ] [time] \n" "qmail-mrtg needs to be called every [time] minutes (i.e. by crontab) - default 305 secs"); flag = *(argv[1] + 1); if (argc == 3) { scan_ulong(argv[2], &u); history = 60 * u; } /* Read input lines sequentially */ buffer_init(&bi, read, 0, bufspace, sizeof(bufspace)); for (;;) { if (getln(&bi, &line, &match, '\n') != 0) _exit(1); if (!match) break; if (!stralloc_0(&line)) _exit(1); seconds = 0; nanoseconds = 0; if (line.s[0] == '@') { /* tai64 timestamp */ for (i = 1; i <= TAI64NLEN; i++) { c = (int)line.s[i]; u = c - '0'; if (u >= 10) { u = c - 'a'; if (u >= 6) break; u += 10; } seconds <<= 4; seconds += nanoseconds >> 28; nanoseconds &= 0xfffffff; nanoseconds <<= 4; nanoseconds += u; } seconds -= 4611686018427387914ULL; seconds = seconds > 0 ? seconds : 0; } else { outs("Error: No TAI64N timestamp available."); _exit(1); } if (enoughtime) { /* default history = 305 sec => evaluate logs every ~5 mins */ if (seconds <= calltime && seconds >= (calltime - history)) { if (flag >= '1' && flag <= '9') mrtg_sendlog(line.s + TAI64NLEN + 2, flag); else if (flag >= 'a' && flag <= 'z') mrtg_smtplog(line.s + TAI64NLEN + 2, flag); else if (flag >= 'A' && flag <= 'Z') mrtg_pop3log(line.s + TAI64NLEN + 2, flag); } } else { if (seconds) { enoughtime++; } else { outs("Warning: Not enough time left between calls"); _exit(1); } } } mrtg_results(flag); _exit(0); }