Back to index

opendkim  2.6.4
Classes | Defines | Functions | Variables
test.c File Reference
#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sysexits.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include "build-config.h"
#include <dkim.h>
#include <dkim-strl.h>
#include <libmilter/mfapi.h>
#include "test.h"
#include "opendkim.h"

Go to the source code of this file.

Classes

struct  test_context

Defines

#define DKIMF_MILTER_PROTOTYPES
#define CRLF   "\r\n"
#define FCLOSE(x)
#define MLFI_OUTPUT(x, y)   ((y) > 1 || ((y) == 1 && (x) != SMFIS_CONTINUE))
#define STRORNULL(x)   ((x) == NULL ? "(null)" : (x))

Functions

int dkimf_test_setpriv (void *ctx, void *ptr)
void * dkimf_test_getpriv (void *ctx)
int dkimf_test_progress (void *ctx)
int dkimf_test_setreply (void *ctx, char *rcode, char *xcode, char *replytxt)
int dkimf_test_insheader (void *ctx, int idx, char *hname, char *hvalue)
int dkimf_test_chgheader (void *ctx, char *hname, int idx, char *hvalue)
int dkimf_test_quarantine (void *ctx, char *reason)
int dkimf_test_addheader (void *ctx, char *hname, char *hvalue)
int dkimf_test_delrcpt (void *ctx, char *addr)
int dkimf_test_addrcpt (void *ctx, char *addr)
char * dkimf_test_getsymval (void *ctx, char *sym)
static int dkimf_testfile (DKIM_LIB *libopendkim, struct test_context *tctx, FILE *f, char *file, _Bool strict, int tverbose)
int dkimf_testfiles (DKIM_LIB *libopendkim, char *flist, uint64_t fixedtime, bool strict, int verbose)

Variables

static char test_c_id [] = "@(#)$Id: test.c,v 1.15 2010/09/02 04:04:20 cm-msk Exp $"
char * milter_status []
char * envfrom []
char * envrcpt []
static int tverbose = 0

Class Documentation

struct test_context

Definition at line 42 of file test.c.

Class Members
void * tc_priv

Define Documentation

#define CRLF   "\r\n"

Definition at line 40 of file test.c.

Definition at line 35 of file test.c.

#define FCLOSE (   x)
Value:
if ((x) != stdin) \
                                   fclose((x));

Definition at line 68 of file test.c.

#define MLFI_OUTPUT (   x,
 
)    ((y) > 1 || ((y) == 1 && (x) != SMFIS_CONTINUE))

Definition at line 70 of file test.c.

#define STRORNULL (   x)    ((x) == NULL ? "(null)" : (x))

Definition at line 71 of file test.c.


Function Documentation

int dkimf_test_addheader ( void *  ctx,
char *  hname,
char *  hvalue 
)

Definition at line 266 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
       {
              fprintf(stdout,
                      "### ADDHEADER: hname='%s' hvalue='%s'\n",
                      STRORNULL(hname), STRORNULL(hvalue));
       }

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_addrcpt ( void *  ctx,
char *  addr 
)

Definition at line 315 of file test.c.

{
       assert(ctx != NULL);
       assert(addr != NULL);

       if (tverbose > 1)
              fprintf(stdout, "### ADDRCPT: '%s'\n", addr);

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_chgheader ( void *  ctx,
char *  hname,
int  idx,
char *  hvalue 
)

Definition at line 214 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
       {
              fprintf(stdout,
                      "### CHGHEADER: hname='%s' idx=%d hvalue='%s'\n",
                      STRORNULL(hname), idx, STRORNULL(hvalue));
       }

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_delrcpt ( void *  ctx,
char *  addr 
)

Definition at line 292 of file test.c.

{
       assert(ctx != NULL);
       assert(addr != NULL);

       if (tverbose > 1)
              fprintf(stdout, "### DELRCPT: '%s'\n", addr);

       return MI_SUCCESS;
}

Here is the caller graph for this function:

void* dkimf_test_getpriv ( void *  ctx)

Definition at line 111 of file test.c.

{
       struct test_context *tc;

       assert(ctx != NULL);

       tc = ctx;

       return tc->tc_priv;
}

Here is the caller graph for this function:

char* dkimf_test_getsymval ( void *  ctx,
char *  sym 
)

Definition at line 343 of file test.c.

{
       static char symout[MAXBUFRSZ];

       assert(ctx != NULL);
       assert(sym != NULL);

       snprintf(symout, sizeof symout, "DEBUG-%s", sym);

       return strdup(symout);
}

Here is the caller graph for this function:

int dkimf_test_insheader ( void *  ctx,
int  idx,
char *  hname,
char *  hvalue 
)

Definition at line 186 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
       {
              fprintf(stdout,
                      "### INSHEADER: idx=%d hname='%s' hvalue='%s'\n",
                      idx, STRORNULL(hname), STRORNULL(hvalue));
       }

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_progress ( void *  ctx)

Definition at line 133 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
              fprintf(stdout, "### PROGRESS\n");

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_quarantine ( void *  ctx,
char *  reason 
)

Definition at line 240 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
       {
              fprintf(stdout,
                      "### QUARANTINE: reason='%s'\n", STRORNULL(reason));
       }

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_setpriv ( void *  ctx,
void *  ptr 
)

Definition at line 88 of file test.c.

{
       struct test_context *tc;

       assert(ctx != NULL);

       tc = ctx;
       tc->tc_priv = ptr;

       return MI_SUCCESS;
}

Here is the caller graph for this function:

int dkimf_test_setreply ( void *  ctx,
char *  rcode,
char *  xcode,
char *  replytxt 
)

Definition at line 157 of file test.c.

{
       assert(ctx != NULL);

       if (tverbose > 1)
       {
              fprintf(stdout,
                      "### SETREPLY: rcode='%s' xcode='%s' replytxt='%s'\n",
                      STRORNULL(rcode), STRORNULL(xcode),
                      STRORNULL(replytxt));
       }

       return MI_SUCCESS;
}

Here is the caller graph for this function:

static int dkimf_testfile ( DKIM_LIB *  libopendkim,
struct test_context tctx,
FILE *  f,
char *  file,
_Bool  strict,
int  tverbose 
) [static]

Definition at line 370 of file test.c.

{
       bool inheaders = TRUE;
       int len = 0;
       int buflen = 0;
       int lineno = 0;
       int hslineno = 0;
       int c;
       DKIM_SIGINFO *sig;
       struct signreq *srlist = NULL;
       DKIM *dkim;
       char *p;
       sfsistat ms;
       struct sockaddr_in sin;
       char buf[MAXBUFRSZ];
       char line[MAXBUFRSZ];

       assert(libopendkim != NULL);
       assert(tctx != NULL);
       assert(f != NULL);

       memset(buf, '\0', sizeof buf);
       memset(line, '\0', sizeof buf);

       ms = mlfi_envfrom((SMFICTX *) tctx, envfrom);
       if (MLFI_OUTPUT(ms, tverbose))
       {
              fprintf(stderr, "%s: %s: mlfi_envfrom() returned %s\n",
                      progname, file, milter_status[ms]);
       }
       if (ms != SMFIS_CONTINUE)
              return EX_SOFTWARE;

       ms = mlfi_envrcpt((SMFICTX *) tctx, envrcpt);
       if (MLFI_OUTPUT(ms, tverbose))
       {
              fprintf(stderr, "%s: %s: mlfi_envrcpt() returned %s\n",
                      progname, file, milter_status[ms]);
       }
       if (ms != SMFIS_CONTINUE)
              return EX_SOFTWARE;

       while (!feof(f))
       {
              if (fgets(line, sizeof line, f) == NULL)
                     break;

              lineno++;

              c = '\0';
              for (p = line; *p != '\0'; p++)
              {
                     if (*p == '\n')
                     {
                            *p = '\0';
                            break;
                     }

                     c = *p;
              }

              if (c != '\r')
              {
                     if (strict)                 /* error */
                     {
                            fprintf(stderr,
                                    "%s: %s: line %d: not CRLF-terminated\n",
                                    progname, file, lineno);
                            return EX_DATAERR;
                     }
              }
              else if (p != line)                /* eat the CR */
              {
                     *(p - 1) = '\0';
              }

              if (inheaders)
              {
                     if (line[0] == '\0')
                     {
                            if (buf[0] != '\0')
                            {
                                   char *colon;

                                   colon = strchr(buf, ':');
                                   if (colon == NULL)
                                   {
                                          fprintf(stderr,
                                                  "%s: %s: line %d: header malformed\n",
                                                  progname, file,
                                                  lineno);
                                          return EX_DATAERR;
                                   }

                                   *colon = '\0';
                                   if (*(colon + 1) == ' ')
                                          colon++;

                                   ms = mlfi_header((SMFICTX *) tctx, buf,
                                                    colon + 1);
                                   if (MLFI_OUTPUT(ms, tverbose))
                                   {
                                          fprintf(stderr,
                                                  "%s: %s: line %d: mlfi_header() returned %s\n",
                                                   progname, file,
                                                   hslineno,
                                                   milter_status[ms]);
                                   }

                                   if (ms != SMFIS_CONTINUE)
                                          return EX_SOFTWARE;
                            }

                            inheaders = FALSE;
                            memset(buf, '\0', sizeof buf);
                            memset(line, '\0', sizeof buf);

                            ms = mlfi_eoh((SMFICTX *) tctx);
                            if (MLFI_OUTPUT(ms, tverbose))
                            {
                                   fprintf(stderr,
                                           "%s: %s: mlfi_eoh() returned %s\n",
                                            progname, file,
                                            milter_status[ms]);
                            }
                            if (ms != SMFIS_CONTINUE)
                                   return EX_SOFTWARE;

                            continue;
                     }

                     if (line[0] == ' ' || line[0] == '\t')
                     {
                            (void) strlcat(buf, CRLF, sizeof buf);

                            if (strlcat(buf, line,
                                        sizeof buf) >= sizeof buf)
                            {
                                   fprintf(stderr,
                                           "%s: %s: line %d: header '%*s...' too large\n",
                                           progname, file, lineno,
                                           20, buf);
                                   return EX_DATAERR;
                            }
                     }
                     else
                     {
                            if (buf[0] != '\0')
                            {
                                   char *colon;

                                   colon = strchr(buf, ':');
                                   if (colon == NULL)
                                   {
                                          fprintf(stderr,
                                                  "%s: %s: line %d: header malformed\n",
                                                  progname, file,
                                                  lineno);
                                          return EX_DATAERR;
                                   }

                                   *colon = '\0';
                                   if (*(colon + 1) == ' ')
                                          colon++;

                                   ms = mlfi_header((SMFICTX *) tctx, buf,
                                                    colon + 1);
                                   if (MLFI_OUTPUT(ms, tverbose))
                                   {
                                          fprintf(stderr,
                                                  "%s: %s: line %d: mlfi_header() returned %s\n",
                                                  progname, file,
                                                  hslineno,
                                                  milter_status[ms]);
                                   }
                                   if (ms != SMFIS_CONTINUE)
                                          return EX_SOFTWARE;
                                   hslineno = 0;
                            }

                            if (hslineno == 0)
                                   hslineno = lineno;

                            strlcpy(buf, line, sizeof buf);
                     }
              }
              else
              {
                     len = strlen(line);

                     if (len + buflen >= (int) sizeof buf - 3)
                     {
                            ms = mlfi_body((SMFICTX *) tctx,
                                           (u_char *) buf,
                                           strlen(buf));
                            if (MLFI_OUTPUT(ms, tverbose))
                            {
                                   fprintf(stderr,
                                           "%s: %s: mlfi_body() returned %s\n",
                                           progname, file,
                                           milter_status[ms]);
                            }
                            if (ms != SMFIS_CONTINUE)
                                   return EX_SOFTWARE;

                            memset(buf, '\0', sizeof buf);
                            buflen = 0;
                     }

                     memcpy(&buf[buflen], line, len);
                     buflen += len;
                     memcpy(&buf[buflen], CRLF, 2);
                     buflen += 2;
              }
       }

       /* unprocessed partial header? */
       if (inheaders && buf[0] != '\0')
       {
              char *colon;

              colon = strchr(buf, ':');
              if (colon == NULL)
              {
                     fprintf(stderr,
                             "%s: %s: line %d: header malformed\n",
                             progname, file, lineno);
                     return EX_DATAERR;
              }

              *colon = '\0';
              if (*(colon + 1) == ' ')
                     colon++;

              ms = mlfi_header((SMFICTX *) tctx, buf, colon + 1);
              if (MLFI_OUTPUT(ms, tverbose))
              {
                     fprintf(stderr,
                             "%s: %s: line %d: mlfi_header() returned %s\n",
                             progname, file, lineno, milter_status[ms]);
              }
              if (ms != SMFIS_CONTINUE)
                     return EX_SOFTWARE;

              ms = mlfi_eoh((SMFICTX *) tctx);
              if (MLFI_OUTPUT(ms, tverbose))
              {
                     fprintf(stderr,
                             "%s: %s: mlfi_eoh() returned %s\n",
                              progname, file, milter_status[ms]);
              }
              if (ms != SMFIS_CONTINUE)
                     return EX_SOFTWARE;

              inheaders = FALSE;
              memset(buf, '\0', sizeof buf);
       }

       /* no headers found */
       if (inheaders)
       {
              fprintf(stderr, "%s: %s: warning: no headers on input\n",
                      progname, file);

              ms = mlfi_eoh((SMFICTX *) tctx);
              if (MLFI_OUTPUT(ms, tverbose))
              {
                     fprintf(stderr, "%s: %s: mlfi_eoh() returned %s\n",
                             progname, file, milter_status[ms]);
              }
              if (ms != SMFIS_CONTINUE)
                     return EX_SOFTWARE;
       }

       /* some body left */
       if (!inheaders && buf[0] != '\0')
       {
              ms = mlfi_body((SMFICTX *) tctx, (u_char *) buf, strlen(buf));
              if (MLFI_OUTPUT(ms, tverbose))
              {
                     fprintf(stderr, "%s: %s: mlfi_body() returned %s\n",
                             progname, file, milter_status[ms]);
              }
              if (ms != SMFIS_CONTINUE)
                     return EX_SOFTWARE;
       }

       ms = mlfi_eom((SMFICTX *) tctx);
       if (MLFI_OUTPUT(ms, tverbose))
       {
              fprintf(stderr, "%s: %s: mlfi_eom() returned %s\n",
                      progname, file, milter_status[ms]);
       }

       dkim = dkimf_getdkim(tctx->tc_priv);
       if (dkim != NULL)
       {
              int mode;

              mode = dkim_getmode(dkim);

              sig = dkim_getsignature(dkim);

              if (sig != NULL)
              {
                     const u_char *domain;
                     const u_char *selector;
                     u_int flags;
                     u_int bh;
                     u_int keysize;

                     domain = dkim_sig_getdomain(sig);
                     selector = dkim_sig_getselector(sig);
                     flags = dkim_sig_getflags(sig);
                     bh = dkim_sig_getbh(sig);
                     dkim_sig_getkeysize(sig, &keysize);

                     if ((flags & DKIM_SIGFLAG_PASSED) != 0 &&
                         bh == DKIM_SIGBH_MATCH)
                     {
                            fprintf(stdout,
                                    "%s: %s: verification (s=%s, d=%s, %d-bit key) succeeded\n",
                                    progname, file, selector, domain,
                                    keysize);
                     }
                     else
                     {
                            const char *err;
                            int errcode;

                            errcode = dkim_sig_geterror(sig);
                            if (errcode == DKIM_SIGERROR_OK &&
                                bh == DKIM_SIGBH_MISMATCH)
                                   err = "body hash mismatch";
                            else
                                   err = dkim_sig_geterrorstr(errcode);

                            if (selector != NULL || domain != NULL)
                            {
#ifdef USE_UNBOUND
                                   char *dnssec;
                                   int dnsseccode = DKIM_DNSSEC_UNKNOWN;
                            
                                   dnsseccode = dkim_sig_getdnssec(sig);

                                   switch (dnsseccode)
                                   {
                                     case DKIM_DNSSEC_BOGUS:
                                          dnssec = "bogus";
                                          break;

                                     case DKIM_DNSSEC_INSECURE:
                                          dnssec = "insecure";
                                          break;

                                     case DKIM_DNSSEC_SECURE:
                                          dnssec = "secure";
                                          break;

                                     case DKIM_DNSSEC_UNKNOWN:
                                     default:
                                          dnssec = "unknown";
                                          break;
                                   }

                                   fprintf(stdout,
                                           "%s: %s: verification (s=%s d=%s, %d-bit key, %s) failed: %s\n",
                                           progname, file, selector,
                                           domain, keysize, dnssec, err);
#else /* USE_UNBOUND */
                                   fprintf(stdout,
                                           "%s: %s: verification (s=%s d=%s, %d-bit key) failed: %s\n",
                                           progname, file, selector,
                                           domain, keysize, err);
#endif /* USE_UNBOUND */
                            }
                            else
                            {
                                   fprintf(stdout,
                                           "%s: %s: verification failed: %s\n",
                                           progname, file, err);
                            }
                     }
              }
              else if (sig == NULL && mode == DKIM_MODE_VERIFY)
              {
                     fprintf(stdout, "%s: %s: message not signed\n",
                             progname, file);
              }
       }

       for (srlist = dkimf_getsrlist(tctx->tc_priv);
            srlist != NULL;
            srlist = srlist->srq_next)
       {
              dkim = srlist->srq_dkim;
              sig = dkim_getsignature(dkim);

              if (sig == NULL)
              {
                     const char *err;

                     err = dkim_geterror(dkim);
                     if (err == NULL)
                            err = "unknown error";

                     fprintf(stdout, "%s: %s: no signature added: %s\n",
                             progname, file, err);
              }
              else
              {
                     if (tverbose < 2)
                     {
                            DKIM_STAT status;
                            size_t hlen;
                            size_t rem;
                            unsigned char hdr[DKIM_MAXHEADER + 1];

                            hlen = strlen(DKIM_SIGNHEADER);
                            rem = sizeof hdr - hlen - 2;
                            memset(hdr, '\0', sizeof hdr);

                            strlcpy((char *) hdr, DKIM_SIGNHEADER ": ",
                                    sizeof hdr);

                            status = dkim_getsighdr(dkim, hdr + hlen + 2,
                                                    rem, hlen + 2);

                            if (status != DKIM_STAT_OK)
                            {
                                   fprintf(stderr,
                                           "%s: %s: dkim_getsighdr(): %s\n",
                                           progname, file,
                                           dkim_getresultstr(status));
                            }
                            else
                            {
                                   fprintf(stdout,
                                           "%s: %s:\n%s\n",
                                           progname, file, hdr);
                            }
                     }
              }
       }

       return EX_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int dkimf_testfiles ( DKIM_LIB *  libopendkim,
char *  flist,
uint64_t  fixedtime,
bool  strict,
int  verbose 
)

Definition at line 834 of file test.c.

{
       char *file;
       char *ctx;
       FILE *f;
       int status;
       sfsistat ms;
       struct test_context *tctx;
       struct sockaddr_in sin;

       assert(libopendkim != NULL);
       assert(flist != NULL);

       tverbose = verbose;

       /* pass fixed signing time to the library */
       if (fixedtime != (uint64_t) -1)
       {
              (void) dkim_options(libopendkim, DKIM_OP_SETOPT,
                                  DKIM_OPTS_FIXEDTIME,
                                  &fixedtime, sizeof fixedtime);
       }

       /* set up a fake SMFICTX */
       tctx = (struct test_context *) malloc(sizeof(struct test_context));
       if (tctx == NULL)
       {
              fprintf(stderr, "%s: malloc(): %s\n", progname,
                      strerror(errno));
              return EX_OSERR;
       }
       tctx->tc_priv = NULL;

       (void) memset(&sin, '\0', sizeof sin);
       sin.sin_family = AF_INET;
       sin.sin_port = htons(time(NULL) % 65536);
       sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

       ms = mlfi_connect((SMFICTX *) tctx, "localhost", (_SOCK_ADDR *) &sin);
       if (MLFI_OUTPUT(ms, tverbose))
       {
              fprintf(stderr, "%s: mlfi_connect() returned %s\n",
                      progname, milter_status[ms]);
       }
       if (ms != SMFIS_CONTINUE)
              return EX_SOFTWARE;

       /* loop through inputs */
       for (file = strtok_r(flist, ",", &ctx);
            file != NULL;
            file = strtok_r(NULL, ",", &ctx))
       {
              /* open the input */
              if (strcmp(file, "-") == 0)
              {
                     f = stdin;
                     file = "(stdin)";
              }
              else
              {
                     f = fopen(file, "r");
                     if (f == NULL)
                     {
                            fprintf(stderr, "%s: %s: fopen(): %s\n",
                                    progname, file, strerror(errno));
                            return EX_UNAVAILABLE;
                     }
              }

              status = dkimf_testfile(libopendkim, tctx, f, file, strict,
                                      tverbose);

              FCLOSE(f);

              if (status != EX_OK)
                     return status;
       }

       ms = mlfi_close((SMFICTX *) tctx);
       if (MLFI_OUTPUT(ms, tverbose))
       {
              fprintf(stderr, "%s: mlfi_close() returned %s\n",
                      progname, milter_status[ms]);
       }

       return EX_OK;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

char* envfrom[]
Initial value:
{
       "<sender@example.org>",
       NULL
}

Definition at line 56 of file test.c.

char* envrcpt[]
Initial value:
{
       "<recipient@example.com>",
       NULL
}

Definition at line 62 of file test.c.

char* milter_status[]
Initial value:
{
       "SMFIS_CONTINUE",
       "SMFIS_REJECT",
       "SMFIS_DISCARD",
       "SMFIS_ACCEPT",
       "SMFIS_TEMPFAIL"
}

Definition at line 47 of file test.c.

char test_c_id[] = "@(#)$Id: test.c,v 1.15 2010/09/02 04:04:20 cm-msk Exp $" [static]

Definition at line 11 of file test.c.

int tverbose = 0 [static]

Definition at line 74 of file test.c.