Back to index

opendkim  2.6.4
opendkim-ar.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2007-2009 Sendmail, Inc. and its suppliers.
00003 **     All rights reserved.
00004 **
00005 **  Copyright (c) 2009, 2011, 2012, The OpenDKIM Project.  All rights reserved.
00006 **
00007 **  $Id: opendkim-ar.c,v 1.5.58.1 2010/10/27 21:43:09 cm-msk Exp $
00008 */
00009 
00010 #ifndef lint
00011 static char opendkim_ar_c_id[] = "@(#)$Id: opendkim-ar.c,v 1.5.58.1 2010/10/27 21:43:09 cm-msk Exp $";
00012 #endif /* !lint */
00013 
00014 #include "build-config.h"
00015 
00016 /* system includes */
00017 #include <sys/types.h>
00018 #include <sys/param.h>
00019 #ifdef HAVE_STDBOOL_H
00020 # include <stdbool.h>
00021 #endif /* HAVE_STDBOOL_H */
00022 #include <ctype.h>
00023 #include <assert.h>
00024 #include <string.h>
00025 #ifdef ARTEST
00026 # include <sysexits.h>
00027 #endif /* ARTEST */
00028 
00029 /* libopendkim includes */
00030 #include <dkim-strl.h>
00031 
00032 /* opendkim includes */
00033 #include "opendkim-ar.h"
00034 
00035 /* macros */
00036 #define       ARES_ENDOF(x)        ((x) + sizeof(x) - 1)
00037 #define       ARES_STRORNULL(x)    ((x) == NULL ? "(null)" : (x))
00038 #define       ARES_TOKENS          ";=."
00039 #define       ARES_TOKENS2         "=."
00040 
00041 #define       ARES_MAXTOKENS              512
00042 
00043 /* tables */
00044 struct lookup
00045 {
00046        char * str;
00047        int    code;
00048 };
00049 
00050 struct lookup methods[] =
00051 {
00052        { "auth",            ARES_METHOD_AUTH },
00053        { "dkim",            ARES_METHOD_DKIM },
00054        { "dkim-adsp",              ARES_METHOD_DKIMADSP },
00055        { "dkim-atps",              ARES_METHOD_DKIMATPS },
00056        { "domainkeys",             ARES_METHOD_DOMAINKEYS },
00057        { "iprev",           ARES_METHOD_IPREV },
00058        { "sender-id",              ARES_METHOD_SENDERID },
00059        { "spf",             ARES_METHOD_SPF },
00060        { NULL,                     ARES_METHOD_UNKNOWN }
00061 };
00062 
00063 struct lookup aresults[] =
00064 {
00065        { "none",            ARES_RESULT_NONE },
00066        { "pass",            ARES_RESULT_PASS },
00067        { "fail",            ARES_RESULT_FAIL },
00068        { "policy",          ARES_RESULT_POLICY },
00069        { "neutral",         ARES_RESULT_NEUTRAL },
00070        { "temperror",              ARES_RESULT_TEMPERROR },
00071        { "permerror",              ARES_RESULT_PERMERROR },
00072        { "nxdomain",        ARES_RESULT_NXDOMAIN },
00073        { "signed",          ARES_RESULT_SIGNED },
00074        { "unknown",         ARES_RESULT_UNKNOWN },
00075        { "discard",         ARES_RESULT_DISCARD },
00076        { "softfail",        ARES_RESULT_SOFTFAIL },
00077        { NULL,                     ARES_RESULT_UNKNOWN }
00078 };
00079 
00080 struct lookup ptypes[] =
00081 {
00082        { "smtp",            ARES_PTYPE_SMTP },
00083        { "header",          ARES_PTYPE_HEADER },
00084        { "body",            ARES_PTYPE_BODY },
00085        { "policy",          ARES_PTYPE_POLICY },
00086        { NULL,                     ARES_PTYPE_UNKNOWN }
00087 };
00088 
00089 /*
00090 **  ARES_TOKENIZE -- tokenize a string
00091 **
00092 **  Parameters:
00093 **     input -- input string
00094 **     outbuf -- output buffer
00095 **     outbuflen -- number of bytes available at "outbuf"
00096 **     tokens -- array of token pointers
00097 **     ntokens -- number of token pointers available at "tokens"
00098 **
00099 **  Return value:
00100 **     -1 -- not enough space at "outbuf" for tokenizing
00101 **     other -- number of tokens identified; may be greater than
00102 **     "ntokens" if there were more tokens found than there were
00103 **     pointers available.
00104 */
00105 
00106 static int
00107 ares_tokenize(u_char *input, u_char *outbuf, size_t outbuflen,
00108               u_char **tokens, int ntokens)
00109 {
00110        _Bool quoted = FALSE;
00111        _Bool escaped = FALSE;
00112        _Bool intok = FALSE;
00113        int n = 0;
00114        int parens = 0;
00115        u_char *p;
00116        u_char *q;
00117        u_char *end;
00118 
00119        assert(input != NULL);
00120        assert(outbuf != NULL);
00121        assert(outbuflen > 0);
00122        assert(tokens != NULL);
00123        assert(ntokens > 0);
00124 
00125        q = outbuf;
00126        end = outbuf + outbuflen - 1;
00127 
00128        for (p = input; *p != '\0' && q <= end; p++)
00129        {
00130               if (escaped)                       /* escape */
00131               {
00132                      if (!intok)
00133                      {
00134                             if (n < ntokens)
00135                                    tokens[n] = q;
00136                             intok = TRUE;
00137                      }
00138 
00139                      *q = *p;
00140                      q++;
00141                      escaped = FALSE;
00142               }
00143               else if (*p == '\\')               /* escape */
00144               {
00145                      escaped = TRUE;
00146               }
00147               else if (*p == '"' && parens == 0) /* quoting */
00148               {
00149                      quoted = !quoted;
00150 
00151                      if (!intok)
00152                      {
00153                             if (n < ntokens)
00154                                    tokens[n] = q;
00155                             intok = TRUE;
00156                      }
00157               }
00158               else if (*p == '(' && !quoted)            /* "(" (comment) */
00159               {
00160                      parens++;
00161 
00162                      if (!intok)
00163                      {
00164                             if (n < ntokens)
00165                                    tokens[n] = q;
00166                             intok = TRUE;
00167                      }
00168 
00169                      *q = *p;
00170                      q++;
00171 
00172               }
00173               else if (*p == ')' && !quoted)            /* ")" (comment) */
00174               {
00175                      if (parens > 0)
00176                      {
00177                             parens--;
00178 
00179                             if (parens == 0)
00180                             {
00181                                    intok = FALSE;
00182                                    n++;
00183 
00184                                    *q = ')';
00185                                    q++;
00186                                    if (q <= end)
00187                                    {
00188                                           *q = '\0';
00189                                           q++;
00190                                    }
00191                             }
00192                      }
00193               }
00194               else if (quoted)                   /* quoted character */
00195               {
00196                      *q = *p;
00197                      q++;
00198               }
00199               else if (isascii(*p) && isspace(*p))      /* whitespace */
00200               {
00201                      if (quoted || parens > 0)
00202                      {
00203                             if (intok)
00204                             {
00205                                    *q = *p;
00206                                    q++;
00207                             }
00208                      }
00209                      else if (intok)
00210                      {
00211                             intok = FALSE;
00212                             *q = '\0';
00213                             q++;
00214                             n++;
00215                      }
00216               }
00217               else if (strchr(ARES_TOKENS, *p) != NULL) /* delimiter */
00218               {
00219                      if (parens > 0)
00220                      {
00221                             *q = *p;
00222                             q++;
00223                             continue;
00224                      }
00225 
00226                      if (intok)
00227                      {
00228                             intok = FALSE;
00229                             *q = '\0';
00230                             q++;
00231                             n++;
00232                      }
00233 
00234                      if (q <= end)
00235                      {
00236                             *q = *p;
00237                             if (n < ntokens)
00238                             {
00239                                    tokens[n] = q;
00240                                    n++;
00241                             }
00242                             q++;
00243                      }
00244 
00245                      if (q <= end)
00246                      {
00247                             *q = '\0';
00248                             q++;
00249                      }
00250               }
00251               else                               /* other */
00252               {
00253                      if (!intok)
00254                      {
00255                             if (n < ntokens)
00256                                    tokens[n] = q;
00257                             intok = TRUE;
00258                      }
00259 
00260                      *q = *p;
00261                      q++;
00262               }
00263        }
00264 
00265        if (q >= end)
00266               return -1;
00267 
00268        if (intok)
00269        {
00270               *q = '\0';
00271               n++;
00272        }
00273 
00274        return n;
00275 }
00276 
00277 /*
00278 **  ARES_TRIMSPACES -- trim trailing whitespace
00279 **
00280 **  Parameters:
00281 **     str -- string to modify
00282 **
00283 **  Return value:
00284 **     None.
00285 */
00286 
00287 static void
00288 ares_trimspaces(u_char *str)
00289 {
00290        u_char *p;
00291        u_char *last;
00292 
00293        assert(str != NULL);
00294 
00295        last = NULL;
00296 
00297        for (p = str; *p != '\0'; p++)
00298        {
00299               if (isascii(*p) && isspace(*p) && last == NULL)
00300               {
00301                      last = p;
00302                      continue;
00303               }
00304 
00305               if (!isascii(*p) || !isspace(*p))
00306                      last = NULL;
00307        }
00308 
00309        if (last != NULL)
00310               *last = '\0';
00311 }
00312 
00313 /*
00314 **  ARES_CONVERT -- convert a string to its code
00315 **
00316 **  Parameters:
00317 **     table -- in which table to look up
00318 **     str -- string to find
00319 **
00320 **  Return value:
00321 **     A code translation of "str".
00322 */
00323 
00324 static int
00325 ares_convert(struct lookup *table, char *str)
00326 {
00327        int c;
00328 
00329        assert(table != NULL);
00330        assert(str != NULL);
00331 
00332        for (c = 0; ; c++)
00333        {
00334               if (table[c].str == NULL ||
00335                   strcmp(table[c].str, str) == 0)
00336                      return table[c].code;
00337        }
00338 
00339        /* NOTREACHED */
00340 }
00341 
00342 #ifdef ARTEST
00343 /*
00344 **  ARES_XCONVERT -- convert a code to its string
00345 **
00346 **  Parameters:
00347 **     table -- in which table to look up
00348 **     code -- code to find
00349 **
00350 **  Return value:
00351 **     A string translation of "code".
00352 */
00353 
00354 static char *
00355 ares_xconvert(struct lookup *table, int code)
00356 {
00357        int c;
00358 
00359        assert(table != NULL);
00360 
00361        for (c = 0; ; c++)
00362        {
00363               if (table[c].str == NULL || table[c].code == code)
00364                      return table[c].str;
00365        }
00366 
00367        /* NOTREACHED */
00368 }
00369 #endif /* ARTEST */
00370 
00371 /*
00372 **  AUTHRES_PARSE -- parse an Authentication-Results: header, return a
00373 **                   structure containing a parsed result
00374 **
00375 **  Parameters:
00376 **     hdr -- NULL-terminated contents of an Authentication-Results:
00377 **            header field
00378 **     ar -- a pointer to a (struct authres) loaded by values after parsing
00379 **  
00380 **  Return value:
00381 **     0 on success, -1 on failure.
00382 */
00383 
00384 int
00385 ares_parse(u_char *hdr, struct authres *ar)
00386 {
00387        _Bool quoted;
00388        int n;
00389        int ntoks;
00390        int c;
00391        int r = 0;
00392        int state;
00393        int prevstate;
00394        u_char tmp[DKIM_MAXHEADER + 2];
00395        u_char *tokens[ARES_MAXTOKENS];
00396 
00397        assert(hdr != NULL);
00398        assert(ar != NULL);
00399 
00400        memset(ar, '\0', sizeof *ar);
00401        memset(tmp, '\0', sizeof tmp);
00402 
00403        ntoks = ares_tokenize(hdr, tmp, sizeof tmp, tokens, ARES_MAXTOKENS);
00404        if (ntoks == -1 || ntoks > ARES_MAXTOKENS)
00405               return -1;
00406 
00407        prevstate = -1;
00408        state = 0;
00409        n = 0;
00410 
00411        quoted = FALSE;
00412 
00413        for (c = 0; c < ntoks; c++)
00414        {
00415               if (tokens[c][0] == '(')           /* comment */
00416                      continue;
00417 
00418               switch (state)
00419               {
00420                 case 0:                          /* authserv-id */
00421                      if (!isascii(tokens[c][0]) ||
00422                          !isalnum(tokens[c][0]))
00423                             return -1;
00424 
00425                      strlcat((char *) ar->ares_host, (char *) tokens[c],
00426                              sizeof ar->ares_host);
00427 
00428                      prevstate = state;
00429                      state = 1;
00430 
00431                      break;
00432 
00433                 case 1:                          /* [version] */
00434                      if (tokens[c][0] == '.' &&
00435                          tokens[c][1] == '\0' && prevstate == 0)
00436                      {
00437                             strlcat((char *) ar->ares_host,
00438                                     (char *) tokens[c],
00439                                     sizeof ar->ares_host);
00440 
00441                             prevstate = state;
00442                             state = 0;
00443 
00444                             break;
00445                      }
00446 
00447                      if (tokens[c][0] == ';')
00448                      {
00449                             prevstate = state;
00450                             state = 3;
00451                      }
00452                      else if (isascii(tokens[c][0]) &&
00453                               isdigit(tokens[c][0]))
00454                      {
00455                             strlcpy((char *) ar->ares_version,
00456                                     (char *) tokens[c],
00457                                     sizeof ar->ares_version);
00458 
00459                             prevstate = state;
00460                             state = 2;
00461                      }
00462                      else
00463                      {
00464                             return -1;
00465                      }
00466 
00467                      break;
00468 
00469                 case 2:                          /* ; */
00470                      if (tokens[c][0] != ';' ||
00471                          tokens[c][1] != '\0')
00472                             return -1;
00473 
00474                      prevstate = state;
00475                      state = 3;
00476 
00477                      break;
00478 
00479                 case 3:                          /* method */
00480                      n++;
00481                      r = 0;
00482 
00483                      ar->ares_result[n - 1].result_method = ares_convert(methods,
00484                                                                          (char *) tokens[c]);
00485                      prevstate = state;
00486                      state = 4;
00487 
00488                      break;
00489 
00490                 case 4:                          /* = */
00491                      if (tokens[c][0] != '=' ||
00492                          tokens[c][1] != '\0')
00493                             return -1;
00494 
00495                      prevstate = state;
00496                      state = 5;
00497 
00498                      break;
00499 
00500                 case 5:                          /* result */
00501                      ar->ares_result[n - 1].result_result = ares_convert(aresults,
00502                                                                          (char *) tokens[c]);
00503                      prevstate = state;
00504                      state = 6;
00505 
00506                      break;
00507 
00508                 case 7:                          /* = (reason) */
00509                      if (tokens[c][0] != '=' ||
00510                          tokens[c][1] != '\0')
00511                             return -1;
00512 
00513                      prevstate = state;
00514                      state = 8;
00515 
00516                      break;
00517 
00518                 case 8:
00519                      strlcpy((char *) ar->ares_result[n - 1].result_reason,
00520                              (char *) tokens[c],
00521                              sizeof ar->ares_result[n - 1].result_reason);
00522 
00523                      prevstate = state;
00524                      state = 9;
00525 
00526                      break;
00527 
00528                 case 6:                          /* reason/propspec */
00529                      if (tokens[c][0] == ';' &&  /* neither */
00530                          tokens[c][1] == '\0')
00531                      {
00532                             prevstate = state;
00533                             state = 3;
00534 
00535                             continue;
00536                      }
00537 
00538                      if (strcasecmp((char *) tokens[c], "reason") == 0)
00539                      {                           /* reason */
00540                             prevstate = state;
00541                             state = 7;
00542 
00543                             continue;
00544                      }
00545                      else
00546                      {
00547                             prevstate = state;
00548                             state = 9;
00549                      }
00550 
00551                      /* FALLTHROUGH */
00552 
00553                 case 9:                          /* ptype */
00554                      if (prevstate == 13 &&
00555                          strchr(ARES_TOKENS2, tokens[c][0]) != NULL &&
00556                          tokens[c][1] == '\0')
00557                      {
00558                             r--;
00559 
00560                             strlcat((char *) ar->ares_result[n - 1].result_value[r],
00561                                     (char *) tokens[c],
00562                                     sizeof ar->ares_result[n - 1].result_value[r]);
00563 
00564                             prevstate = state;
00565                             state = 13;
00566 
00567                             continue;
00568                      }
00569 
00570                      if (tokens[c][0] == ';' &&
00571                          tokens[c][1] == '\0')
00572                      {
00573                             prevstate = state;
00574                             state = 3;
00575 
00576                             continue;
00577                      }
00578                      else
00579                      {
00580                             ares_ptype_t x;
00581 
00582                             x = ares_convert(ptypes, (char *) tokens[c]);
00583                             if (x == ARES_PTYPE_UNKNOWN)
00584                                    return -1;
00585 
00586                             ar->ares_result[n - 1].result_ptype[r] = x;
00587 
00588                             prevstate = state;
00589                             state = 10;
00590                      }
00591 
00592                      break;
00593 
00594                 case 10:                         /* . */
00595                      if (tokens[c][0] != '.' ||
00596                          tokens[c][1] != '\0')
00597                             return -1;
00598 
00599                      prevstate = state;
00600                      state = 11;
00601 
00602                      break;
00603 
00604                 case 11:                         /* property */
00605                      strlcpy((char *) ar->ares_result[n - 1].result_property[r],
00606                              (char *) tokens[c],
00607                              sizeof ar->ares_result[n - 1].result_property[r]);
00608 
00609                      prevstate = state;
00610                      state = 12;
00611 
00612                      break;
00613 
00614                 case 12:                         /* = */
00615                      if (tokens[c][0] != '=' ||
00616                          tokens[c][1] != '\0')
00617                             return -1;
00618 
00619                      prevstate = state;
00620                      state = 13;
00621 
00622                      break;
00623 
00624                 case 13:                         /* value */
00625                      strlcat((char *) ar->ares_result[n - 1].result_value[r],
00626                              (char *) tokens[c],
00627                              sizeof ar->ares_result[n - 1].result_value[r]);
00628                      r++;
00629                      ar->ares_result[n - 1].result_props = r;
00630 
00631                      prevstate = state;
00632                      state = 9;
00633 
00634                      break;
00635               }
00636        }
00637 
00638        /* error out on non-terminal states */
00639        if (state == 4 || state == 7 || state == 10 ||
00640            state == 11 || state == 12)
00641               return -1;
00642 
00643        ar->ares_count = n;
00644 
00645        return 0;
00646 }
00647 
00648 #ifdef ARTEST
00649 /*
00650 **  MAIN -- program mainline
00651 **
00652 **  Parameters:
00653 **     argc, argv -- the usual
00654 **
00655 **  Return value:
00656 **     EX_USAGE or EX_OK
00657 */
00658 
00659 # define NTOKENS 256
00660 
00661 int
00662 main(int argc, char **argv)
00663 {
00664        int c;
00665        int d;
00666        int status;
00667        char *p;
00668        char *progname;
00669        struct authres ar;
00670        u_char buf[1024];
00671        u_char *toks[NTOKENS];
00672 
00673        progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
00674 
00675        if (argc != 2)
00676        {
00677               printf("%s: usage: %s header-value\n", progname, progname);
00678               return EX_USAGE;
00679        }
00680 
00681        c = ares_tokenize(argv[1], buf, sizeof buf, toks, NTOKENS);
00682        for (d = 0; d < c; d++)
00683               printf("token %d = '%s'\n", d, toks[d]);
00684 
00685        printf("\n");
00686 
00687        status = ares_parse(argv[1], &ar);
00688        if (status == -1)
00689        {
00690               printf("%s: ares_parse() returned -1\n", progname);
00691               return EX_OK;
00692        }
00693 
00694        printf("%d result%s found\n", ar.ares_count,
00695               ar.ares_count == 1 ? "" : "s");
00696 
00697        printf("authserv-id '%s'\n", ar.ares_host);
00698        printf("version '%s'\n", ar.ares_version);
00699 
00700        for (c = 0; c < ar.ares_count; c++)
00701        {
00702               printf("result #%d, %d propert%s\n", c,
00703                      ar.ares_result[c].result_props,
00704                      ar.ares_result[c].result_props == 1 ? "y" : "ies");
00705 
00706               printf("\tmethod \"%s\"\n",
00707                      ares_xconvert(methods,
00708                                    ar.ares_result[c].result_method));
00709               printf("\tresult \"%s\"\n",
00710                      ares_xconvert(aresults,
00711                                    ar.ares_result[c].result_result));
00712               printf("\treason \"%s\"\n", ar.ares_result[c].result_reason);
00713 
00714               for (d = 0; d < ar.ares_result[c].result_props; d++)
00715               {
00716                      printf("\tproperty #%d\n", d);
00717                      printf("\t\tptype \"%s\"\n",
00718                             ares_xconvert(ptypes,
00719                                           ar.ares_result[c].result_ptype[d]));
00720                      printf("\t\tproperty \"%s\"\n",
00721                             ar.ares_result[c].result_property[d]);
00722                      printf("\t\tvalue \"%s\"\n",
00723                             ar.ares_result[c].result_value[d]);
00724               }
00725        }
00726 }
00727 #endif /* ARTEST */