Back to index

opendkim  2.6.4
opendkim-stats.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2010, 2011, The OpenDKIM Project.  All rights reserved.
00003 **
00004 **  $Id: opendkim-stats.c,v 1.19.2.1 2010/10/27 21:43:09 cm-msk Exp $
00005 */
00006 
00007 #ifndef lint
00008 static char opendkim_stats_c_id[] = "$Id: opendkim-stats.c,v 1.19.2.1 2010/10/27 21:43:09 cm-msk Exp $";
00009 #endif /* ! lint */
00010 
00011 /* system includes */
00012 #include <sys/param.h>
00013 #include <sys/types.h>
00014 #include <sysexits.h>
00015 #include <assert.h>
00016 #include <unistd.h>
00017 #include <string.h>
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 #include <time.h>
00021 #include <errno.h>
00022 
00023 /* OpenDKIM includes */
00024 #include "build-config.h"
00025 #include "stats.h"
00026 
00027 /* libopendkim includes */
00028 #include <dkim.h>
00029 
00030 /* macros, definitions */
00031 #define       MAXLINE              2048
00032 
00033 #ifndef MAX
00034 # define MAX(x,y)    ((x) > (y) ? (x) : (y))
00035 #endif /* ! MAX */
00036 
00037 /* globals */
00038 char *progname;
00039 
00040 /*
00041 **  USAGE -- print usage message and exit
00042 **
00043 **  Parameters:
00044 **     None.
00045 **
00046 **  Return value:
00047 **     EX_USAGE
00048 */
00049 
00050 int
00051 usage(void)
00052 {
00053        fprintf(stderr, "%s: usage: %s [statsfile]\n", progname, progname);
00054 
00055        return EX_USAGE;
00056 }
00057 
00058 /*
00059 **  MAIN -- program mainline
00060 **
00061 **  Parameters:
00062 **     argc, argv -- the usual
00063 **
00064 **  Return value:
00065 **     Exit status.
00066 */
00067 
00068 int
00069 main(int argc, char **argv)
00070 {
00071        int c;
00072        int n;
00073        int m = 0;
00074        int s = 0;
00075        int ms = 0;
00076        int nfields = 0;
00077        int line;
00078        int syntax = 0;
00079        char *p;
00080        char *infile = NULL;
00081        char **fields = NULL;
00082        FILE *in;
00083        char buf[MAXLINE + 1];
00084 
00085        progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
00086 
00087        if (argc == 2)
00088               infile = argv[1];
00089        else if (argc != 1)
00090               return usage();
00091 
00092        if (infile != NULL)
00093        {
00094               in = fopen(infile, "r");
00095               if (in == NULL)
00096               {
00097                      fprintf(stderr, "%s: %s: fopen(): %s\n", progname,
00098                              infile, strerror(errno));
00099                      return EX_UNAVAILABLE;
00100               }
00101        }
00102        else
00103        {
00104               in = stdin;
00105        }
00106 
00107        /* initialize stuff */
00108        memset(buf, '\0', sizeof buf);
00109        line = 0;
00110 
00111        /* read lines from stdin */
00112        while (fgets(buf, sizeof buf - 1, in) != NULL)
00113        {
00114               line++;
00115 
00116               /* eat the newline */
00117               for (p = buf; *p != '\0'; p++)
00118               {
00119                      if (*p == '\n')
00120                      {
00121                             *p = '\0';
00122                             break;
00123                      }
00124               }
00125 
00126               /* first byte identifies the record type */
00127               c = buf[0];
00128 
00129               /* reset fields array */
00130               if (fields != NULL)
00131                      memset(fields, '\0', sizeof(char *) * nfields);
00132 
00133               /* now break out the fields */
00134               n = 0;
00135               for (p = strtok(buf + 1, "\t");
00136                    p != NULL;
00137                    p = strtok(NULL, "\t"))
00138               {
00139                      if (nfields == n)
00140                      {
00141                             int newnf;
00142                             size_t newsz;
00143                             char **new;
00144 
00145                             newnf = MAX(nfields * 2, 8);
00146                             newsz = sizeof(char *) * newnf;
00147 
00148                             if (nfields == 0)
00149                                    new = (char **) malloc(newsz);
00150                             else
00151                                    new = (char **) realloc(fields, newsz);
00152 
00153                             if (new == NULL)
00154                             {
00155                                    fprintf(stderr,
00156                                            "%s: %salloc(): %s\n",
00157                                            progname,
00158                                            fields == NULL ? "m" : "re",
00159                                            strerror(errno));
00160                                    return EX_OSERR;
00161                             }
00162 
00163                             nfields = newnf;
00164                             fields = new;
00165                      }
00166 
00167                      fields[n++] = p;
00168               }
00169 
00170               /* processing section for version tags */
00171               if (c == 'V')
00172               {
00173                      int inversion;
00174 
00175                      if (n != 1)
00176                      {
00177                             fprintf(stderr,
00178                                     "%s: unexpected version field count (%d) at input line %d\n",
00179                                     progname, n, line);
00180 
00181                             continue;
00182                      }
00183 
00184                      inversion = atoi(fields[0]);
00185                      if (inversion != DKIMS_VERSION)
00186                      {
00187                             fprintf(stderr,
00188                                     "%s: unknown version (%d) at input line %d\n",
00189                                     progname, inversion, line);
00190 
00191                             continue;
00192                      }
00193               }
00194 
00195               /* processing section for messages */
00196               else if (c == 'M')
00197               {
00198                      time_t rtime;
00199                      char *adsp;
00200                      char *adsppf;
00201                      char *ct;
00202                      char *cte;
00203                      char *atps;
00204                      char *spam;
00205 
00206                      if (n != DKIMS_MI_MAX + 1)
00207                      {
00208                             fprintf(stderr,
00209                                     "%s: unexpected message field count (%d) at input line %d\n",
00210                                     progname, n, line);
00211                             continue;
00212                      }
00213 
00214                      /* format the data */
00215                      rtime = (time_t) atoi(fields[DKIMS_MI_MSGTIME]);
00216 
00217                      atps = "not checked";
00218 #ifdef _FFR_ATPS
00219                      if (fields[DKIMS_MI_ATPS][0] == '0')
00220                             atps = "no match";
00221                      else if (fields[DKIMS_MI_ATPS][0] == '1')
00222                             atps = "match";
00223 #endif /* _FFR_ATPS */
00224 
00225                      spam = "unknown";
00226 #ifdef _FFR_REPUTATION
00227                      if (fields[DKIMS_MI_SPAM][0] == '0')
00228                             spam = "not spam";
00229                      else if (fields[DKIMS_MI_SPAM][0] == '1')
00230                             spam = "spam";
00231 #endif /* _FFR_REPUTATION */
00232 
00233                      if (ms > 0)
00234                      {
00235                             fprintf(stdout, "\n");
00236                             ms = 0;
00237                      }
00238 
00239                      fprintf(stdout, "Job %s at %s (size %s)\n\treceived via %s at %s\tfrom domain = '%s'\n",
00240                              fields[DKIMS_MI_JOBID],
00241                              fields[DKIMS_MI_REPORTER],
00242                              fields[DKIMS_MI_MSGLEN],
00243                              fields[DKIMS_MI_IPADDR],
00244                              ctime(&rtime),
00245                              fields[DKIMS_MI_FROMDOMAIN]);
00246 
00247 #ifdef _FFR_ATPS
00248                      fprintf(stdout, "\tATPS %s\n", atps);
00249 #endif /* _FFR_ATPS */
00250 
00251 #ifdef _FFR_REPUTATION
00252                      fprintf(stdout, "\tSpam: %s\n", spam);
00253 #endif /* _FFR_REPUTATION */
00254 
00255                      m++;
00256               }
00257 
00258               /* processing section for signatures */
00259               else if (c == 'S')
00260               {
00261                      char *sigstat;
00262                      char *siglen;
00263                      char *dnssec;
00264 
00265                      if (n != DKIMS_SI_MAX + 1)
00266                      {
00267                             fprintf(stderr,
00268                                     "%s: unexpected signature field count (%d) at input line %d\n",
00269                                     progname, n, line);
00270                             continue;
00271                      }
00272                      else if (m == 0)
00273                      {
00274                             fprintf(stderr,
00275                                     "%s: signature record before message record at input line %d\n",
00276                                     progname, line);
00277                             continue;
00278                      }
00279 
00280                      ms++;
00281 
00282                      /* format output */
00283                      if (fields[DKIMS_SI_PASS][0] == '1')
00284                             sigstat = "PASSED";
00285                      else if (fields[DKIMS_SI_FAIL_BODY][0] == '1')
00286                             sigstat = "FAILED (body changed)";
00287                      else if (atoi(fields[DKIMS_SI_SIGERROR]) == DKIM_SIGERROR_KEYREVOKED)
00288                             sigstat = "REVOKED";
00289                      else if (fields[DKIMS_SI_SIGERROR][0] != '0')
00290                             sigstat = "ERROR";
00291                      else
00292                             sigstat = "UNKNOWN";
00293 
00294                      if (fields[DKIMS_SI_SIGLENGTH][0] == '-')
00295                             siglen = "(whole message)";
00296                      else
00297                             siglen = fields[DKIMS_SI_SIGLENGTH];
00298 
00299                      switch (fields[DKIMS_SI_DNSSEC][0])
00300                      {
00301                        case '-':
00302                             dnssec = "UNKNOWN";
00303                             break;
00304 
00305                        case '0':
00306                             dnssec = "BOGUS";
00307                             break;
00308 
00309                        case '1':
00310                             dnssec = "INSECURE";
00311                             break;
00312 
00313                        case '2':
00314                             dnssec = "SECURE";
00315                             break;
00316                      }
00317 
00318                      syntax = atoi(fields[DKIMS_SI_SIGERROR]);
00319                      syntax = (syntax == DKIM_SIGERROR_VERSION ||
00320                                syntax == DKIM_SIGERROR_DOMAIN ||
00321                                syntax == DKIM_SIGERROR_TIMESTAMPS ||
00322                                syntax == DKIM_SIGERROR_INVALID_HC ||
00323                                syntax == DKIM_SIGERROR_INVALID_BC ||
00324                                syntax == DKIM_SIGERROR_MISSING_A ||
00325                                syntax == DKIM_SIGERROR_INVALID_A ||
00326                                syntax == DKIM_SIGERROR_MISSING_H ||
00327                                syntax == DKIM_SIGERROR_INVALID_L ||
00328                                syntax == DKIM_SIGERROR_INVALID_Q ||
00329                                syntax == DKIM_SIGERROR_INVALID_QO ||
00330                                syntax == DKIM_SIGERROR_MISSING_D ||
00331                                syntax == DKIM_SIGERROR_EMPTY_D ||
00332                                syntax == DKIM_SIGERROR_MISSING_S ||
00333                                syntax == DKIM_SIGERROR_EMPTY_S ||
00334                                syntax == DKIM_SIGERROR_MISSING_B ||
00335                                syntax == DKIM_SIGERROR_EMPTY_B ||
00336                                syntax == DKIM_SIGERROR_CORRUPT_B ||
00337                                syntax == DKIM_SIGERROR_MISSING_BH ||
00338                                syntax == DKIM_SIGERROR_EMPTY_BH ||
00339                                syntax == DKIM_SIGERROR_CORRUPT_BH ||
00340                                syntax == DKIM_SIGERROR_EMPTY_H ||
00341                                syntax == DKIM_SIGERROR_INVALID_H ||
00342                                syntax == DKIM_SIGERROR_TOOLARGE_L ||
00343                                syntax == DKIM_SIGERROR_MBSFAILED);
00344 
00345                      fprintf(stdout, "\tSignature %d from %s\n\t\t%s\n\t\tsigned bytes: %s\n\t\tSignature properties: %s\n\t\tKey properties: %s %s\n\t\tDNSSEC status: %s\n",
00346                              ms,
00347                              fields[DKIMS_SI_DOMAIN],
00348                              sigstat, siglen,
00349                              atoi(fields[DKIMS_SI_SIGERROR]) == DKIM_SIGERROR_FUTURE ? "t=future"
00350                                                                                     : "",
00351                              syntax != 0 ? "syntax" : "",
00352                              atoi(fields[DKIMS_SI_SIGERROR]) == DKIM_SIGERROR_NOKEY ? "NXDOMAIN"
00353                                                                                     : "",
00354                              dnssec);
00355 
00356                      s++;
00357               }
00358 
00359 #ifdef _FFR_STATSEXT
00360               /* processing section for extension data */
00361               else if (c == 'X')
00362               {
00363                      fprintf(stdout, "\tExtension data: %s=%s\n",
00364                              fields[0], fields[1]);
00365               }
00366 #endif /* _FFR_STATSEXT */
00367  
00368               /* unknown record type */
00369               else
00370               {
00371                      fprintf(stderr,
00372                              "%s: unknown record type '%c' at input line %d\n",
00373                              progname, c, line);
00374               }
00375        }
00376 
00377        if (ferror(in))
00378        {
00379               fprintf(stderr, "%s: fgets(): %s at input line %d\n", progname,
00380                       strerror(errno), line);
00381        }
00382 
00383        if (infile != NULL)
00384               fclose(in);
00385 
00386        fprintf(stdout, "%s: %d message%s, %d signature%s processed\n",
00387                progname, m, m == 0 ? "" : "s", s, s == 0 ? "" : "s");
00388 
00389        return EX_OK;
00390 }