"Fossies" - the Fresh Open Source Software Archive

Member "libspf2-1.2.10/src/libspf2/spf_compile.c" (20 Feb 2012, 38068 Bytes) of package /linux/privat/libspf2-1.2.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spf_compile.c" see the Fossies "Dox" file reference documentation.

    1 /* 
    2  * This program is free software; you can redistribute it and/or modify
    3  * it under the terms of either:
    4  * 
    5  *   a) The GNU Lesser General Public License as published by the Free
    6  *    Software Foundation; either version 2.1, or (at your option) any
    7  *    later version,
    8  * 
    9  *   OR
   10  * 
   11  *   b) The two-clause BSD license.
   12  *
   13  * These licenses can be found with the distribution in the file LICENSES
   14  */
   15 
   16 #include "spf_sys_config.h"
   17 #include "spf_internal.h"
   18 
   19 
   20 #ifdef STDC_HEADERS
   21 # include <stdio.h>     /* stdin / stdout */
   22 # include <stdlib.h>       /* malloc / free */
   23 # include <ctype.h>     /* isupper / tolower */
   24 #endif
   25 
   26 #ifdef HAVE_INTTYPES_H
   27 #include <inttypes.h>
   28 #endif
   29 
   30 #ifdef HAVE_STRING_H
   31 # include <string.h>       /* strstr / strdup */
   32 #else
   33 # ifdef HAVE_STRINGS_H
   34 #  include <strings.h>     /* strstr / strdup */
   35 # endif
   36 #endif
   37 
   38 
   39 
   40 #undef SPF_ALLOW_DEPRECATED_DEFAULT
   41 
   42 #include "spf.h"
   43 #include "spf_internal.h"
   44 #include "spf_response.h"
   45 #include "spf_record.h"
   46 
   47 typedef
   48 enum SPF_cidr_enum {
   49     CIDR_NONE, CIDR_OPTIONAL, CIDR_ONLY
   50 } SPF_cidr_t;
   51 
   52 typedef
   53 enum SPF_domspec_enum {
   54     DOMSPEC_NONE, DOMSPEC_OPTIONAL, DOMSPEC_REQUIRED
   55 } SPF_domspec_t;
   56 
   57 /**
   58  * This is greater than any possible total mechanism or modifier.
   59  *   SPF_MAX_MOD_LEN  + SPF_MAX_STR_LEN
   60  *   SPF_MAX_MECH_LEN + SPF_MAX_STR_LEN
   61  */
   62 #define SPF_RECORD_BUFSIZ     4096
   63 
   64 #define ALIGN_DECL(decl) union { double d; long l; decl } __attribute__((aligned(_ALIGN_SZ))) u
   65 #define ALIGNED_DECL(var) u.var
   66 
   67 
   68 
   69 typedef
   70 struct SPF_mechtype_struct
   71 {
   72     unsigned char        mech_type;
   73     unsigned char        is_dns_mech;
   74     SPF_domspec_t        has_domainspec;
   75     SPF_cidr_t           has_cidr;
   76 } SPF_mechtype_t;
   77 
   78 static const SPF_mechtype_t spf_mechtypes[] = {
   79     { MECH_UNKNOWN,     FALSE,      DOMSPEC_NONE,       CIDR_NONE },
   80     { MECH_A,           TRUE,       DOMSPEC_OPTIONAL,   CIDR_OPTIONAL },
   81     { MECH_MX,          TRUE,       DOMSPEC_OPTIONAL,   CIDR_OPTIONAL },
   82     { MECH_PTR,         TRUE,       DOMSPEC_OPTIONAL,   CIDR_NONE },
   83     { MECH_INCLUDE,     TRUE,       DOMSPEC_REQUIRED,   CIDR_NONE },
   84     { MECH_IP4,         FALSE,      DOMSPEC_REQUIRED,   CIDR_OPTIONAL },
   85     { MECH_IP6,         FALSE,      DOMSPEC_REQUIRED,   CIDR_OPTIONAL },
   86     { MECH_EXISTS,      TRUE,       DOMSPEC_REQUIRED,   CIDR_NONE },
   87     { MECH_ALL,         FALSE,      DOMSPEC_NONE,       CIDR_NONE },
   88     { MECH_REDIRECT,    TRUE,       DOMSPEC_REQUIRED,   CIDR_NONE },
   89 };
   90 
   91 #define spf_num_mechanisms \
   92         sizeof(spf_mechtypes) / sizeof(spf_mechtypes[0])
   93 
   94 static const SPF_mechtype_t *
   95 SPF_mechtype_find(int mech_type)
   96 {
   97     size_t       i;
   98     for (i = 0; i < spf_num_mechanisms; i++) {
   99         if (spf_mechtypes[i].mech_type == mech_type)
  100             return &spf_mechtypes[i];
  101     }
  102     return NULL;
  103 }
  104 
  105 __attribute__((warn_unused_result))
  106 static int
  107 SPF_c_ensure_capacity(void **datap, size_t *sizep, size_t length)
  108 {
  109     size_t       size = *sizep;
  110     if (length > size)
  111         size = length + (length / 4);
  112     if (size > *sizep) {
  113         void    *tmp = realloc(*datap, size);
  114         if (!tmp)
  115             return -1;
  116         // memset(tmp + *sizep, 'C', (size - *sizep));
  117         *datap = tmp;
  118         *sizep = size;
  119     }
  120     return 0;
  121 }
  122 
  123 /**
  124  * Parses an ip6 CIDR.
  125  *
  126  * Called with src pointing to the '/'.
  127  *
  128  * If a struct for IP addresses is added which itself contains a
  129  * CIDR field, then this must be modified to take a (cidr *) rather
  130  * than a (SPF_data_cidr_t *)
  131  */
  132 static SPF_errcode_t
  133 SPF_c_parse_cidr_ip6(SPF_response_t *spf_response,
  134                 unsigned char *maskp,
  135                 const char *src)
  136 {
  137     int      mask;
  138 
  139     /*
  140     if (spf_server->debug > 2)
  141         SPF_debugf("Parsing ip6 CIDR starting at %s", src);
  142     */
  143 
  144     mask = strtoul(src + 1, NULL, 10);
  145 
  146     if (mask > 128) {
  147         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
  148                         NULL, src,
  149                         "Invalid IPv6 CIDR netmask (>128)");
  150     }
  151     else if (mask == 0) {
  152         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
  153                         NULL, src,
  154                         "Invalid IPv6 CIDR netmask (=0)");
  155     }
  156     else if (mask == 128) {
  157         mask = 0;
  158     }
  159 
  160     *maskp = mask;
  161 
  162     return SPF_E_SUCCESS;
  163 }
  164 
  165 /**
  166  * Parses an ip4 CIDR.
  167  *
  168  * Called with src pointing to the '/', the second '/' if we are in
  169  * a '//' notation, so that the digits start at src + 1.
  170  *
  171  * SPF_c_parse_cidr relies on the behaviour of strtoul terminating
  172  * on a '/' as well as a nul byte here.
  173  */
  174 static SPF_errcode_t
  175 SPF_c_parse_cidr_ip4(SPF_response_t *spf_response,
  176                 unsigned char *maskp,
  177                 const char *src)
  178 {
  179     int      mask;
  180 
  181     /*
  182     if (spf_server->debug > 2)
  183         SPF_debugf("Parsing ip4 CIDR starting at %s", src);
  184     */
  185 
  186     mask = strtoul(src + 1, NULL, 10);
  187 
  188     if ( mask > 32 ) {
  189         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
  190                         NULL, src,
  191                         "Invalid IPv4 CIDR netmask (>32)");
  192     }
  193     else if ( mask == 0 ) {
  194         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
  195                         NULL, src,
  196                         "Invalid IPv4 CIDR netmask (=0)");
  197     }
  198     else if ( mask == 32 ) {
  199         mask = 0;
  200     }
  201 
  202     *maskp = mask;
  203 
  204     return SPF_E_SUCCESS;
  205 }
  206 
  207 /**
  208  * Parses an SPF CIDR.
  209  *
  210  * Modifies *src_len if a CIDR is found.
  211  */
  212 static SPF_errcode_t
  213 SPF_c_parse_cidr(SPF_response_t *spf_response,
  214                 SPF_data_cidr_t *data,
  215                 const char *src, size_t *src_len)
  216 {
  217     SPF_errcode_t    err;
  218     size_t           idx;
  219 
  220     memset(data, 0, sizeof(SPF_data_cidr_t));
  221     data->parm_type = PARM_CIDR;
  222 
  223     /* Find the beginning of the CIDR length notation.
  224      * XXX This assumes that there is a non-digit in the string.
  225      * This is always true for SPF records with domainspecs, since
  226      * there has to be an = or a : before it. */
  227     idx = *src_len - 1;
  228     while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
  229         idx--;
  230 
  231     /* Something is frying my brain and I can't pull an invariant
  232      * out of this suitable for resetting *endp. So I nested the
  233      * 'if's instead. Perhaps I'll manage to refactor later. */
  234 
  235     /* If we have a slash which isn't the last character. */
  236     if (idx < (*src_len - 1) && src[idx] == '/') {
  237         if (idx > 0 && src[idx - 1] == '/') {
  238             /* get IPv6 CIDR length */
  239             err = SPF_c_parse_cidr_ip6(spf_response, &data->ipv6, &src[idx]);
  240             if (err)
  241                 return err;
  242             /* now back up and see if there is a ipv4 cidr length */
  243             *src_len = idx - 1; /* The index of the first '/' */
  244             idx = *src_len - 1; /* Last character of what is before. */
  245             while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
  246                 idx--;
  247 
  248             /* get IPv4 CIDR length */
  249             if (idx < (*src_len - 1) && src[idx] == '/') {
  250                 /* - we know that strtoul terminates on the
  251                  * '/' so we don't need to null-terminate the
  252                  * input string. */
  253                 err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
  254                 if (err)
  255                     return err;
  256                 *src_len = idx;
  257             }
  258         }
  259         else {
  260             /* get IPv4 CIDR length */
  261             err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
  262             if (err)
  263                 return err;
  264             *src_len = idx;
  265         }
  266     }
  267 
  268     return SPF_E_SUCCESS;
  269 }
  270 
  271 static SPF_errcode_t
  272 SPF_c_parse_var(SPF_response_t *spf_response, SPF_data_var_t *data,
  273                 const char *src, int is_mod)
  274 {
  275     const char      *token;
  276     const char      *p;
  277     char             c;
  278     int              val;
  279 
  280     memset(data, 0, sizeof(SPF_data_var_t));
  281 
  282     p = src;
  283 
  284     /* URL encoding */
  285     c = *p;
  286     if ( isupper( (unsigned char)( c ) ) )
  287     {
  288         data->url_encode = TRUE;
  289         c = tolower(c);
  290     }
  291     else
  292         data->url_encode = FALSE;
  293 
  294 #define SPF_CHECK_IN_MODIFIER() \
  295         if ( !is_mod ) \
  296             return SPF_response_add_error_ptr(spf_response, \
  297                         SPF_E_INVALID_VAR, NULL, p, \
  298                         "'%c' macro is only valid in modifiers", c);
  299 
  300     switch ( c )
  301     {
  302     case 'l':               /* local-part of envelope-sender */
  303         data->parm_type = PARM_LP_FROM;
  304         break;
  305 
  306     case 's':               /* envelope-sender              */
  307         data->parm_type = PARM_ENV_FROM;
  308         break;
  309 
  310     case 'o':               /* envelope-domain              */
  311         data->parm_type = PARM_DP_FROM;
  312         break;
  313 
  314     case 'd':               /* current-domain               */
  315         data->parm_type = PARM_CUR_DOM;
  316         break;
  317 
  318     case 'i':               /* SMTP client IP               */
  319         data->parm_type = PARM_CLIENT_IP;
  320         break;
  321 
  322     case 'c':               /* SMTP client IP (pretty)      */
  323         SPF_CHECK_IN_MODIFIER();
  324         data->parm_type = PARM_CLIENT_IP_P;
  325         break;
  326 
  327     case 't':               /* time in UTC epoch secs       */
  328         SPF_CHECK_IN_MODIFIER();
  329         data->parm_type = PARM_TIME;
  330         break;
  331 
  332     case 'p':               /* SMTP client domain name      */
  333         data->parm_type = PARM_CLIENT_DOM;
  334         break;
  335 
  336     case 'v':               /* IP ver str - in-addr/ip6     */
  337         data->parm_type = PARM_CLIENT_VER;
  338         break;
  339 
  340     case 'h':               /* HELO/EHLO domain             */
  341         data->parm_type = PARM_HELO_DOM;
  342         break;
  343 
  344     case 'r':               /* receiving domain             */
  345         SPF_CHECK_IN_MODIFIER();
  346         data->parm_type = PARM_REC_DOM;
  347         break;
  348 
  349     default:
  350         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_VAR,
  351                         NULL, p,
  352                         "Unknown variable '%c'", c);
  353     }
  354     p++;
  355     token = p;
  356         
  357     /* get the number of subdomains to truncate to */
  358     val = 0;
  359     while ( isdigit( (unsigned char)( *p ) ) )
  360     {
  361         val *= 10;
  362         val += *p - '0';
  363         p++;
  364     }
  365     if ( val > 128  ||  (val <= 0 && p != token) )
  366         return SPF_response_add_error_ptr(spf_response, SPF_E_BIG_SUBDOM,
  367                         NULL, token,
  368                         "Subdomain truncation depth too large");
  369     data->num_rhs = val;
  370     token = p;
  371         
  372     /* should the string be reversed? */
  373     if ( *p == 'r' )
  374     {
  375         data->rev = 1;
  376         p++;
  377     }
  378     else
  379         data->rev = FALSE;
  380     token = p;
  381 
  382 
  383     /* check for delimiters */
  384     data->delim_dot = FALSE;
  385     data->delim_dash = FALSE;
  386     data->delim_plus = FALSE;
  387     data->delim_equal = FALSE;
  388     data->delim_bar = FALSE;
  389     data->delim_under = FALSE;
  390 
  391     /*vi:{*/
  392     if ( *p == '}' )
  393         data->delim_dot = TRUE;
  394 
  395     /*vi:{*/
  396     while( *p != '}' )
  397     {
  398         token = p;
  399         switch( *p )
  400         {
  401         case '.':
  402             data->delim_dot = TRUE;
  403             break;
  404                 
  405         case '-':
  406             data->delim_dash = TRUE;
  407             break;
  408                 
  409         case '+':
  410             data->delim_plus = TRUE;
  411             break;
  412                 
  413         case '=':
  414             data->delim_equal = TRUE;
  415             break;
  416                 
  417         case '|':
  418             data->delim_bar = TRUE;
  419             break;
  420                 
  421         case '_':
  422             data->delim_under = TRUE;
  423             break;
  424 
  425         default:
  426             return SPF_response_add_error_ptr(spf_response,
  427                             SPF_E_INVALID_DELIM, NULL, p,
  428                             "Invalid delimiter '%c'", *p);
  429         }
  430         p++;
  431     }
  432     p++;
  433     token = p;
  434 
  435 
  436     return SPF_E_SUCCESS;
  437 }
  438 
  439 
  440         /* Sorry, Wayne. */
  441 #define SPF_ADD_LEN_TO(_val, _len, _max) do { \
  442             if ( (_val) + _align_sz(_len) > (_max) ) {              \
  443                 return SPF_response_add_error_ptr(spf_response,     \
  444                     big_err, NULL, src,                             \
  445                     "SPF domainspec too long "                      \
  446                     "(%d chars, %d max)",                           \
  447                     (_val) + (_len), _max);                         \
  448             }                                                       \
  449             (_val) += _align_sz(_len);                              \
  450         } while(0)
  451 
  452 #define SPF_INIT_STRING_LITERAL(_avail) do { \
  453             data->ds.parm_type = PARM_STRING;                       \
  454             data->ds.len = 0;                                       \
  455             /* Magic numbers for x/Nc in gdb. */                    \
  456             data->ds.__unused0 = 0xba; data->ds.__unused1 = 0xbe;   \
  457             dst = SPF_data_str( data );                             \
  458             ds_avail = _avail;                                      \
  459             ds_len = 0;                                             \
  460         } while(0)
  461 
  462 #define SPF_ENSURE_STRING_AVAIL(_len)   do {        \
  463             if (ds_len + _len > ds_avail)           \
  464                 return SPF_response_add_error_ptr(spf_response, \
  465                                 SPF_E_BIG_STRING, NULL, src,    \
  466                             "String literal fragment too long " \
  467                             "(%d chars, %d max)",               \
  468                             ds_len, ds_avail);                  \
  469         } while(0)
  470 
  471 #define SPF_FINI_STRING_LITERAL()       do { \
  472             if ( ds_len > 0 ) {                                     \
  473                 if ( ds_len > SPF_MAX_STR_LEN ) {                   \
  474                     return SPF_response_add_error_ptr(spf_response,     \
  475                                     SPF_E_BIG_STRING, NULL, src,    \
  476                                 "String literal too long "          \
  477                                 "(%d chars, %d max)",               \
  478                                 ds_len, SPF_MAX_STR_LEN);           \
  479                 }                                                   \
  480                 data->ds.len = ds_len;                              \
  481                 len = sizeof( *data ) + ds_len;                     \
  482                 SPF_ADD_LEN_TO(*data_used, len, data_avail);        \
  483                 data = SPF_data_next( data );                       \
  484                 ds_len = 0;                                         \
  485             }                                                       \
  486         } while(0)
  487 
  488 /**
  489  * Parses an SPF macro string.
  490  *
  491  * Note that we cannot write data_avail bytes from data, since we
  492  * might be called with a modified data pointer. We MUST compare
  493  * data_used with data_avail.
  494  *
  495  * @param spf_server The SPF server on whose behalf the record is being compiled.
  496  * @param spf_response The SPF response in which to store errors.
  497  * @param data Output buffer pointer.
  498  * @param data_used Output parameter for amount of data written to output buffer.
  499  * @param data_avail Input parameter for size of output buffer.
  500  * @param src Input buffer pointer.
  501  * @param src_len Input buffer length.
  502  * @param big_err The error code to return on an over-length condition.
  503  * @param is_mod True if this is a modifier.
  504  */
  505 static SPF_errcode_t
  506 SPF_c_parse_macro(SPF_server_t *spf_server,
  507                 SPF_response_t *spf_response,
  508                 SPF_data_t *data, size_t *data_used, size_t data_avail,
  509                 const char *src, size_t src_len,
  510                 SPF_errcode_t big_err,
  511                 int is_mod)
  512 {
  513     SPF_errcode_t        err;
  514             /* Generic parsing iterators and boundaries */
  515     size_t               idx;
  516     size_t               len;
  517             /* For parsing strings. */
  518     char                *dst;
  519     size_t               ds_avail;
  520     size_t               ds_len;
  521 
  522     if (spf_server->debug)
  523         SPF_debugf("Parsing macro starting at %s", src);
  524 
  525 #if 0
  526     if ((void *)data != _align_ptr((void *)data))
  527         SPF_errorf("Data pointer %p is not aligned: Cannot compile.",
  528         data);
  529 #endif
  530 
  531     /*
  532      * Create the data blocks
  533      */
  534     idx = 0;
  535 
  536     /* Initialise the block as a string. If ds_len == 0 later, we
  537      * will just clobber it. */
  538     SPF_INIT_STRING_LITERAL(data_avail - *data_used);
  539 
  540     // while ( p != end ) {
  541     while (idx < src_len) {
  542         if (spf_server->debug > 3)
  543             SPF_debugf("Current data is at %p", data);
  544         /* Either the unit is terminated by a space, or we hit a %.
  545          * We should only hit a space if we run past src_len. */
  546         len = strcspn(&src[idx], " %"); // XXX Also tab?
  547         if (len > 0) {              /* An optimisation */
  548             /* Don't over-run into the CIDR. */
  549             if (idx + len > src_len)
  550                 len = src_len - idx;
  551             if (spf_server->debug > 3)
  552                 SPF_debugf("Adding string literal (%lu): '%*.*s'",
  553                                 (unsigned long)len,
  554                                 (int)len, (int)len, &src[idx]);
  555             /* XXX Bounds-check here. */
  556             SPF_ENSURE_STRING_AVAIL(len);
  557             memcpy(dst, &src[idx], len);
  558             ds_len += len;
  559             dst += len;
  560             idx += len;
  561 
  562             /* If len == 0 then we never entered the while(). Thus
  563              * if idx == src_len, then len != 0 and we reach this test.
  564              */
  565         }
  566         /* However, this logic is overcomplex and I am a simpleton,
  567          * so I have moved it out of the condition above. */
  568         if (idx == src_len)
  569             break;
  570 
  571         /* Now, we must have a %-escape code, since if we hit a
  572          * space, then we are at the end.
  573          * Incrementing idx consumes the % we hit first, and then
  574          * we switch on the following character, which also
  575          * increments idx. */
  576         idx++;
  577         switch (src[idx]) {
  578         case '%':
  579             if (spf_server->debug > 3)
  580                 SPF_debugf("Adding literal %%");
  581             SPF_ENSURE_STRING_AVAIL(1);
  582             *dst++ = '%';
  583             ds_len++;
  584             idx++;
  585             break;
  586             
  587         case '_':
  588             if (spf_server->debug > 3)
  589                 SPF_debugf("Adding literal space");
  590             SPF_ENSURE_STRING_AVAIL(1);
  591             *dst++ = ' ';
  592             ds_len++;
  593             idx++;
  594             break;
  595 
  596         case '-':
  597             if (spf_server->debug > 3)
  598                 SPF_debugf("Adding escaped space");
  599             SPF_ENSURE_STRING_AVAIL(3);
  600             *dst++ = '%'; *dst++ = '2'; *dst++ = '0';
  601             ds_len += 3;
  602             idx++;
  603             break;
  604 
  605         default:
  606             if (spf_server->debug > 3)
  607                 SPF_debugf("Adding illegal %%-follower '%c' at %d",
  608                 src[idx], idx);
  609             /* SPF spec says to treat it as a literal, not
  610              * SPF_E_INVALID_ESC */
  611             /* FIXME   issue a warning? */
  612             SPF_ENSURE_STRING_AVAIL(1);
  613             *dst++ = '%';
  614             ds_len++;
  615             break;
  616 
  617         case '{':  /*vi:}*/
  618             SPF_FINI_STRING_LITERAL();
  619             if (spf_server->debug > 3)
  620                 SPF_debugf("Adding macro, data is at %p", data);
  621 
  622             /* this must be a variable */
  623             idx++;
  624             err = SPF_c_parse_var(spf_response, &data->dv, &src[idx], is_mod);
  625             if (err != SPF_E_SUCCESS)
  626                 return err;
  627             idx += strcspn(&src[idx], "} ");
  628             if (src[idx] == '}')
  629                 idx++;
  630             else if (src[idx] == ' ')
  631                 return SPF_response_add_error_ptr(spf_response,
  632                         SPF_E_INVALID_VAR,
  633                         src, &src[idx],
  634                         "Unterminated variable?");
  635 
  636 
  637             len = SPF_data_len(data);
  638             SPF_ADD_LEN_TO(*data_used, len, data_avail);
  639             data = SPF_data_next( data );
  640             if (spf_server->debug > 3)
  641                 SPF_debugf("Next data is at %p", data);
  642 
  643             SPF_INIT_STRING_LITERAL(data_avail - *data_used);
  644 
  645             break;
  646         }
  647     }
  648 
  649     SPF_FINI_STRING_LITERAL();
  650 
  651     return SPF_E_SUCCESS;
  652 
  653 }
  654 
  655 /* What a fuck-ugly prototype. */
  656 /**
  657  * Parses an SPF domainspec.
  658  *
  659  * @param spf_server The SPF server on whose behalf the record is being compiled.
  660  * @param spf_response The SPF response in which to store errors.
  661  * @param data Output buffer pointer.
  662  * @param data_used Output parameter for amount of data written to output buffer.
  663  * @param data_avail Input parameter for size of output buffer.
  664  * @param src Input buffer pointer.
  665  * @param src_len Input buffer length.
  666  * @param big_err The error code to return on an over-length condition.
  667  * @param cidr_ok True if a CIDR mask is permitted on this domainspec.
  668  * @param is_mod True if this is a modifier.
  669  */
  670 static SPF_errcode_t
  671 SPF_c_parse_domainspec(SPF_server_t *spf_server,
  672                 SPF_response_t *spf_response,
  673                 SPF_data_t *data, size_t *data_used, size_t data_avail,
  674                 const char *src, size_t src_len,
  675                 SPF_errcode_t big_err,
  676                 SPF_cidr_t cidr_ok, int is_mod)
  677 {
  678     SPF_errcode_t        err;
  679             /* Generic parsing iterators and boundaries */
  680     size_t              len;
  681 
  682     if (spf_server->debug)
  683         SPF_debugf("Parsing domainspec starting at %s, cidr is %s",
  684                         src,
  685                         cidr_ok == CIDR_OPTIONAL ? "optional" :
  686                         cidr_ok == CIDR_ONLY ? "only" :
  687                         cidr_ok == CIDR_NONE ? "forbidden" :
  688                         "ERROR!"
  689                         );
  690 
  691     /*
  692      * create the CIDR length info
  693      */
  694     if (cidr_ok == CIDR_OPTIONAL || cidr_ok == CIDR_ONLY) {
  695         err = SPF_c_parse_cidr(spf_response, &data->dc, src, &src_len);
  696         if (err != SPF_E_SUCCESS)
  697             return err;
  698         if (data->dc.ipv4 != 0  ||  data->dc.ipv6 != 0) {
  699             len = SPF_data_len(data);
  700             SPF_ADD_LEN_TO(*data_used, len, data_avail);
  701             data = SPF_data_next(data);
  702         }
  703 
  704         if (cidr_ok == CIDR_ONLY && src_len > 0) {
  705             /* We had a mechanism followed by a '/', thus it HAS to be
  706              * a CIDR, and the peculiar-looking error message is
  707              * justified. However, we don't know _which_ CIDR. */
  708             return SPF_response_add_error_ptr(spf_response,
  709                             SPF_E_INVALID_CIDR,
  710                             NULL, src,
  711                             "Invalid CIDR after mechanism");
  712         }
  713     }
  714 
  715     return SPF_c_parse_macro(spf_server, spf_response,
  716             data, data_used, data_avail,
  717             src, src_len, big_err, is_mod);
  718 }
  719 
  720 
  721 /**
  722  * Parses the data for an ip4 mechanism.
  723  *
  724  * When this method is called, start points to the ':'.
  725  */
  726 static SPF_errcode_t
  727 SPF_c_parse_ip4(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
  728 {
  729     const char          *end;
  730     const char          *p;
  731 
  732     char                 buf[ INET6_ADDRSTRLEN ];
  733     size_t               len;
  734     SPF_errcode_t        err;
  735 
  736     unsigned char        mask;
  737     struct in_addr      *addr;
  738 
  739     start++;
  740     len = strcspn(start, " ");
  741     end = start + len;
  742     p = end - 1;
  743 
  744     mask = 0;
  745     while (isdigit( (unsigned char)(*p) ))
  746         p--;
  747     if (p != (end - 1) && *p == '/') {
  748         err = SPF_c_parse_cidr_ip4(spf_response, &mask, p);
  749         if (err)
  750             return err;
  751         end = p;
  752     }
  753     mech->mech_len = mask;
  754 
  755     len = end - start;
  756     if ( len > sizeof( buf ) - 1 )
  757         return SPF_E_INVALID_IP4;
  758 
  759     memcpy( buf, start, len );
  760     buf[ len ] = '\0';
  761     addr = SPF_mech_ip4_data(mech);
  762     err = inet_pton( AF_INET, buf, addr );
  763     if ( err <= 0 )
  764         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP4,
  765                         NULL, buf, NULL);
  766 
  767     return SPF_E_SUCCESS;
  768 }
  769 
  770 /**
  771  * Parses the data for an ip6 mechanism.
  772  *
  773  * When this method is called, start points to the ':'.
  774  */
  775 static SPF_errcode_t
  776 SPF_c_parse_ip6(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
  777 {
  778     const char          *end;
  779     const char          *p;
  780 
  781     char                 buf[ INET6_ADDRSTRLEN ];
  782     size_t               len;
  783     int                  err;
  784 
  785     unsigned char        mask;
  786     struct in6_addr     *addr;
  787 
  788     start++;
  789     len = strcspn(start, " ");
  790     end = start + len;
  791     p = end - 1;
  792 
  793     mask = 0;
  794     while (isdigit( (unsigned char)(*p) ))
  795         p--;
  796     if (p != (end - 1) && *p == '/') {
  797         err = SPF_c_parse_cidr_ip6(spf_response, &mask, p);
  798         if (err)
  799             return err;
  800         end = p;
  801     }
  802     mech->mech_len = mask;
  803 
  804     len = end - start;
  805     if ( len > sizeof( buf ) - 1 )
  806         return SPF_E_INVALID_IP6;
  807 
  808     memcpy( buf, start, len );
  809     buf[ len ] = '\0';
  810     addr = SPF_mech_ip6_data(mech);
  811     err = inet_pton( AF_INET6, buf, addr );
  812     if ( err <= 0 )
  813         return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP6,
  814                         NULL, buf, NULL);
  815 
  816     return SPF_E_SUCCESS;
  817 }
  818 
  819 
  820 /* XXX TODO: Make this take (const char *) instead of (const char **)
  821  * because the caller ignores the modified value. */
  822 __attribute__((warn_unused_result))
  823 static SPF_errcode_t
  824 SPF_c_mech_add(SPF_server_t *spf_server,
  825                 SPF_record_t *spf_record, SPF_response_t *spf_response,
  826                 const SPF_mechtype_t *mechtype, int prefix,
  827                 const char **mech_value)
  828 {
  829     /* If this buffer is an irregular size, intel gcc does not align
  830      * it properly, and all hell breaks loose. */
  831 ALIGN_DECL(
  832     char                 buf[SPF_RECORD_BUFSIZ];
  833 );
  834     SPF_mech_t          *spf_mechanism = (SPF_mech_t *)ALIGNED_DECL(buf);
  835     SPF_data_t          *data;
  836     size_t               data_len;
  837     size_t               len;
  838     size_t               src_len;
  839 
  840     SPF_errcode_t        err;
  841 
  842     memset(u.buf, 'B', sizeof(u.buf));  /* Poison the buffer. */
  843     memset(spf_mechanism, 0, sizeof(SPF_mech_t));
  844 
  845     if (spf_server->debug)
  846         SPF_debugf("SPF_c_mech_add: type=%d, value=%s",
  847                         mechtype->mech_type, *mech_value);
  848 
  849     spf_mechanism->prefix_type = prefix;
  850     spf_mechanism->mech_type = mechtype->mech_type;
  851     spf_mechanism->mech_len = 0;
  852 
  853     len = sizeof( SPF_mech_t );
  854 
  855     if ( spf_record->mech_len + len > SPF_MAX_MECH_LEN )
  856         return SPF_E_BIG_MECH;
  857 
  858     data = SPF_mech_data(spf_mechanism);
  859     data_len = 0;
  860 
  861     src_len = strcspn(*mech_value, " ");
  862 
  863     switch (mechtype->mech_type) {
  864         /* We know the properties of IP4 and IP6. */
  865             case MECH_IP4:
  866             if (**mech_value == ':') {
  867                 err = SPF_c_parse_ip4(spf_response, spf_mechanism, *mech_value);
  868                 data_len = sizeof(struct in_addr);
  869             }
  870             else {
  871                 err = SPF_E_MISSING_OPT;
  872                 SPF_response_add_error_ptr(spf_response, err,
  873                         NULL, *mech_value,
  874                         "Mechanism requires a value.");
  875             }
  876             break;
  877 
  878         case MECH_IP6:
  879             if (**mech_value == ':') {
  880                 err = SPF_c_parse_ip6(spf_response, spf_mechanism, *mech_value);
  881                 data_len = sizeof(struct in6_addr);
  882             }
  883             else {
  884                 err = SPF_E_MISSING_OPT;
  885                 SPF_response_add_error_ptr(spf_response, err,
  886                         NULL, *mech_value,
  887                         "Mechanism requires a value.");
  888             }
  889             break;
  890 
  891         default:
  892             if (**mech_value == ':' || **mech_value == '=') {
  893                 if (mechtype->has_domainspec == DOMSPEC_NONE) {
  894                     err = SPF_E_INVALID_OPT;
  895                     SPF_response_add_error_ptr(spf_response, err,
  896                             NULL, *mech_value,
  897                             "Mechanism does not permit a value.");
  898                 }
  899                 else {
  900                     (*mech_value)++; src_len--;
  901                     err = SPF_c_parse_domainspec(spf_server,
  902                                     spf_response,
  903                                     data, &data_len, SPF_MAX_MECH_LEN,
  904                                     *mech_value, src_len,
  905                                     SPF_E_BIG_MECH,
  906                                     mechtype->has_cidr, FALSE);
  907                 }
  908             }
  909             else if (**mech_value == '/') {
  910                 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
  911                     err = SPF_E_MISSING_OPT;
  912                     SPF_response_add_error_ptr(spf_response, err,
  913                             NULL, *mech_value,
  914                             "Mechanism requires a value.");
  915                 }
  916                 else if (mechtype->has_cidr == CIDR_NONE) {
  917                     err = SPF_E_INVALID_CIDR;
  918                     SPF_response_add_error_ptr(spf_response, err,
  919                             NULL, *mech_value,
  920                             "Mechanism does not permit a CIDR.");
  921                 }
  922                 else {
  923                     err = SPF_c_parse_domainspec(spf_server,
  924                                     spf_response,
  925                                     data, &data_len, SPF_MAX_MECH_LEN,
  926                                     *mech_value, src_len,
  927                                     SPF_E_BIG_MECH,
  928                                     CIDR_ONLY, FALSE);
  929                 }
  930             }
  931             else if (**mech_value == ' '  ||  **mech_value == '\0') {
  932                 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
  933                     err = SPF_E_MISSING_OPT;
  934                     SPF_response_add_error_ptr(spf_response, err,
  935                             NULL, *mech_value,
  936                             "Mechanism requires a value.");
  937                 }
  938                 else {
  939                     err = SPF_E_SUCCESS;
  940                 }
  941             }
  942             else {
  943                 err = SPF_E_SYNTAX;
  944                 SPF_response_add_error_ptr(spf_response, err,
  945                         NULL, *mech_value,
  946                         "Unknown character '%c' after mechanism.",
  947                         **mech_value);
  948             }
  949 
  950             /* Does not apply to ip4/ip6 */
  951             spf_mechanism->mech_len = data_len;
  952             break;
  953     }
  954 
  955     len += data_len;
  956 
  957     /* Copy the thing in. */
  958     if (err == SPF_E_SUCCESS) {
  959         if (mechtype->is_dns_mech)
  960             spf_record->num_dns_mech++;
  961         if (SPF_c_ensure_capacity((void **)&spf_record->mech_first,
  962                             &spf_record->mech_size,
  963                             spf_record->mech_len + len) < 0)
  964             return SPF_response_add_error_ptr(spf_response,
  965                             SPF_E_NO_MEMORY,
  966                             NULL, NULL,
  967                             "Failed to allocate memory for mechanism");
  968         memcpy( (char *)spf_record->mech_first + spf_record->mech_len,
  969             spf_mechanism,
  970             len);
  971         spf_record->mech_len += len;
  972         spf_record->num_mech++;
  973     }
  974 
  975     *mech_value += src_len;
  976 
  977     return err;
  978 }
  979 
  980 __attribute__((warn_unused_result))
  981 static SPF_errcode_t
  982 SPF_c_mod_add(SPF_server_t *spf_server,
  983                 SPF_record_t *spf_record, SPF_response_t *spf_response,
  984                 const char *mod_name, size_t name_len,
  985                 const char **mod_value)
  986 {
  987     /* If this buffer is an irregular size, intel gcc does not align
  988      * it properly, and all hell breaks loose. */
  989 ALIGN_DECL(
  990     char                 buf[SPF_RECORD_BUFSIZ];
  991 );
  992     SPF_mod_t           *spf_modifier = (SPF_mod_t *)u.buf;
  993     SPF_data_t          *data;
  994     size_t               data_len;
  995     size_t               len;
  996     size_t               src_len;
  997 
  998     SPF_errcode_t        err;
  999 
 1000     if (spf_server->debug)
 1001         SPF_debugf("Adding modifier name=%lu@%s, value=%s",
 1002                         (unsigned long)name_len, mod_name, *mod_value);
 1003 
 1004     memset(u.buf, 'A', sizeof(u.buf));
 1005     memset(spf_modifier, 0, sizeof(SPF_mod_t));
 1006 
 1007     if ( name_len > SPF_MAX_MOD_LEN )
 1008         return SPF_E_BIG_MOD;
 1009 
 1010     spf_modifier->name_len = name_len;
 1011     spf_modifier->data_len = 0;
 1012 
 1013     /* So that spf_modifier + len == SPF_mod_data(spf_modifier) */
 1014     len = _align_sz(sizeof( SPF_mod_t ) + name_len);
 1015 
 1016     if ( spf_record->mod_len + len > SPF_MAX_MOD_LEN )
 1017         return SPF_E_BIG_MOD;
 1018 
 1019     memcpy(SPF_mod_name(spf_modifier), mod_name, name_len);
 1020 
 1021     data = SPF_mod_data(spf_modifier);
 1022     data_len = 0;
 1023 
 1024     src_len = strcspn(*mod_value, " ");
 1025 
 1026     err = SPF_c_parse_macro(spf_server,
 1027                     spf_response,
 1028                     data, &data_len, SPF_MAX_MOD_LEN,
 1029                     *mod_value, src_len,
 1030                     SPF_E_BIG_MOD,
 1031                     TRUE );
 1032     spf_modifier->data_len = data_len;
 1033     len += data_len;
 1034 
 1035     /* Copy the thing in. */
 1036     if (err == SPF_E_SUCCESS) {
 1037         if (SPF_c_ensure_capacity((void **)&spf_record->mod_first,
 1038                             &spf_record->mod_size,
 1039                             spf_record->mod_len + len) < 0)
 1040             return SPF_response_add_error_ptr(spf_response,
 1041                             SPF_E_NO_MEMORY,
 1042                             NULL, NULL,
 1043                             "Failed to allocate memory for modifier");
 1044         memcpy( (char *)spf_record->mod_first + spf_record->mod_len,
 1045             spf_modifier,
 1046             len);
 1047         spf_record->mod_len += len;
 1048         spf_record->num_mod++;
 1049     }
 1050 
 1051     return err;
 1052 }
 1053 
 1054 static void
 1055 SPF_record_lint(SPF_server_t *spf_server,
 1056                                 SPF_response_t *spf_response,
 1057                                 SPF_record_t *spf_record)
 1058 {
 1059     SPF_data_t      *d, *data_end;
 1060 
 1061     char        *s;
 1062     char        *s_end;
 1063 
 1064     int          found_non_ip;
 1065     int          found_valid_tld;
 1066     
 1067     SPF_mech_t  *mech;
 1068     SPF_data_t  *data;
 1069     
 1070     int             i;
 1071 
 1072     /* FIXME  these warnings suck.  Should call SPF_id2str to give more
 1073      * context. */
 1074 
 1075     mech = spf_record->mech_first;
 1076     for (i = 0;
 1077                     i < spf_record->num_mech;
 1078                         i++,
 1079                         mech = SPF_mech_next( mech ) )
 1080     {
 1081         if ( ( mech->mech_type == MECH_ALL
 1082                || mech->mech_type == MECH_REDIRECT )
 1083              && i != spf_record->num_mech - 1 )
 1084         {
 1085             SPF_response_add_warn(spf_response, SPF_E_MECH_AFTER_ALL,
 1086                             "Mechanisms found after the \"all:\" "
 1087                             "mechanism will be ignored.");
 1088         }
 1089 
 1090         /*
 1091          * if we are dealing with a mechanism, make sure that the data
 1092          * at least looks like a valid host name.
 1093          *
 1094          * note: this routine isn't called to handle ip4: and ip6: and all
 1095          * the other mechanisms require a host name.
 1096          */
 1097 
 1098         if ( mech->mech_type == MECH_IP4
 1099              || mech->mech_type == MECH_IP6 )
 1100             continue;
 1101 
 1102         data = SPF_mech_data( mech );
 1103         data_end = SPF_mech_end_data( mech );
 1104         if ( data == data_end )
 1105             continue;
 1106 
 1107         if ( data->dc.parm_type == PARM_CIDR )
 1108         {
 1109             data = SPF_data_next( data );
 1110             if ( data == data_end )
 1111                 continue;
 1112         }
 1113         
 1114 
 1115         found_valid_tld = FALSE;
 1116         found_non_ip = FALSE;
 1117 
 1118         for( d = data; d < data_end; d = SPF_data_next( d ) )
 1119         {
 1120             switch( d->dv.parm_type )
 1121             {
 1122             case PARM_CIDR:
 1123                 SPF_error( "Multiple CIDR parameters found" );
 1124                 break;
 1125                 
 1126             case PARM_CLIENT_IP:
 1127             case PARM_CLIENT_IP_P:
 1128             case PARM_LP_FROM:
 1129                 found_valid_tld = FALSE;
 1130                 break;
 1131 
 1132             case PARM_STRING:
 1133                 found_valid_tld = FALSE;
 1134 
 1135                 s = SPF_data_str( d );
 1136                 s_end = s + d->ds.len;
 1137                 for( ; s < s_end; s++ ) {
 1138                     if ( !isdigit( (unsigned char)( *s ) ) && *s != '.' && *s != ':' )
 1139                         found_non_ip = TRUE;
 1140 
 1141                     if ( *s == '.' ) 
 1142                         found_valid_tld = TRUE;
 1143                     else if ( !isalpha( (unsigned char)( *s ) ) )
 1144                         found_valid_tld = FALSE;
 1145                 }
 1146                 break;
 1147 
 1148             default:
 1149                 found_non_ip = TRUE;
 1150                 found_valid_tld = TRUE;
 1151             
 1152                 break;
 1153             }
 1154         }
 1155 
 1156         if ( !found_valid_tld || !found_non_ip ) {
 1157             if ( !found_non_ip )
 1158                 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_IP,
 1159                             "Invalid hostname (an IP address?)");
 1160             else if ( !found_valid_tld )
 1161                 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_TLD,
 1162                             "Hostname has a missing or invalid TLD");
 1163         }
 1164 
 1165     }
 1166 
 1167     /* FIXME check for modifiers that should probably be mechanisms */
 1168 }
 1169 
 1170 
 1171 
 1172 /**
 1173  * The SPF compiler.
 1174  *
 1175  * It converts the SPF record in string format that is easy for people
 1176  * to deal with into a compact binary format that is easy for
 1177  * computers to deal with.
 1178  */
 1179 SPF_errcode_t
 1180 SPF_record_compile(SPF_server_t *spf_server,
 1181                                 SPF_response_t *spf_response, 
 1182                                 SPF_record_t **spf_recordp,
 1183                                 const char *record)
 1184 {
 1185     const SPF_mechtype_t*mechtype;
 1186     SPF_record_t        *spf_record;
 1187     SPF_error_t         *spf_error;
 1188     SPF_errcode_t        err;
 1189     
 1190     const char          *name_start;
 1191     size_t               name_len;
 1192 
 1193     const char          *val_start;
 1194     const char          *val_end;
 1195     
 1196     int                  prefix;
 1197 
 1198     const char          *p;
 1199     int                  i;
 1200 
 1201 
 1202     /*
 1203      * make sure we were passed valid data to work with
 1204      */
 1205     SPF_ASSERT_NOTNULL(spf_server);
 1206     SPF_ASSERT_NOTNULL(spf_recordp);
 1207     SPF_ASSERT_NOTNULL(record);
 1208 
 1209     if (spf_server->debug)
 1210         SPF_debugf("Compiling record %s", record);
 1211 
 1212     /*
 1213      * and make sure that we will always set *spf_recordp
 1214      * just incase we can't find a valid SPF record
 1215      */
 1216     *spf_recordp = NULL;
 1217 
 1218     /*
 1219      * See if this is record is even an SPF record
 1220      */
 1221     p = record;
 1222 
 1223     if (strncasecmp(p, SPF_VER_STR, sizeof(SPF_VER_STR) - 1) != 0)
 1224         return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
 1225                         NULL, p,
 1226                         "Could not find a valid SPF record");
 1227     p += sizeof( SPF_VER_STR ) - 1;
 1228 
 1229     if ( *p != '\0' && *p != ' ' )
 1230         return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
 1231                         NULL, p,
 1232                         "Could not find a valid SPF record");
 1233 
 1234     spf_record = SPF_record_new(spf_server, record);
 1235     if (spf_record == NULL) {
 1236         *spf_recordp = NULL;
 1237         return SPF_response_add_error_ptr(spf_response, SPF_E_NO_MEMORY,
 1238                         NULL, p,
 1239                         "Failed to allocate an SPF record");
 1240     }
 1241     spf_record->version = 1;
 1242     *spf_recordp = spf_record;
 1243 
 1244     /*
 1245      * parse the SPF record
 1246      */
 1247     while (*p != '\0') {
 1248         /* TODO WARN: If it's a \n or a \t */
 1249         /* skip to the next token */
 1250         while (*p == ' ')
 1251             p++;
 1252 
 1253         if (*p == '\0')
 1254             break;
 1255 
 1256         /* see if we have a valid prefix */
 1257         prefix = PREFIX_UNKNOWN;
 1258         switch (*p) {
 1259             case '+':
 1260                 prefix = PREFIX_PASS;
 1261                 p++;
 1262                 break;
 1263                 
 1264             case '-':
 1265                 prefix = PREFIX_FAIL;
 1266                 p++;
 1267                 break;
 1268                 
 1269             case '~':
 1270                 prefix = PREFIX_SOFTFAIL;
 1271                 p++;
 1272                 break;
 1273                 
 1274             case '?':
 1275                 prefix = PREFIX_NEUTRAL;
 1276                 p++;
 1277                 break;
 1278 
 1279             default:
 1280                 while (ispunct((unsigned char)(*p))) {
 1281                     SPF_response_add_error_ptr(spf_response,
 1282                                     SPF_E_INVALID_PREFIX, NULL, p,
 1283                                     "Invalid prefix '%c'", *p);
 1284                         p++;
 1285                 }
 1286                 break;
 1287         }
 1288 
 1289         name_start = p;
 1290         val_end = name_start + strcspn(p, " ");
 1291 
 1292         /* get the mechanism/modifier */
 1293         if ( ! isalpha( (unsigned char)*p ) ) {
 1294             /* We could just bail on this one. */
 1295             SPF_response_add_error_ptr(spf_response,
 1296                             SPF_E_INVALID_CHAR, NULL, p,
 1297                             "Invalid character at start of mechanism");
 1298             p += strcspn(p, " ");
 1299             continue;
 1300         }
 1301         while ( isalnum( (unsigned char)*p ) || *p == '_' || *p == '-' )
 1302             p++;
 1303 
 1304 /* TODO: These or macros like them are used in several places. Merge. */
 1305 #define STREQ_SIZEOF(a, b) \
 1306                 (strncasecmp((a), (b), sizeof( (b) ) - 1) == 0)
 1307 #define STREQ_SIZEOF_N(a, b, n) \
 1308                 (((n) == sizeof(b) - 1) && (strncasecmp((a),(b),(n)) == 0))
 1309 
 1310         /* See if we have a modifier or a prefix */
 1311         name_len = p - name_start;
 1312 
 1313         if (spf_server->debug)
 1314             SPF_debugf("Name starts at  %s", name_start);
 1315 
 1316         switch ( *p ) 
 1317         {
 1318         case ':':
 1319         case '/':
 1320         case ' ':
 1321         case '\0':
 1322         compile_mech:       /* A bona fide label */
 1323             
 1324             /*
 1325              * parse the mechanism
 1326              */
 1327 
 1328             /* mechanisms default to PREFIX_PASS */
 1329             if ( prefix == PREFIX_UNKNOWN )
 1330                 prefix = PREFIX_PASS;
 1331 
 1332             if ( STREQ_SIZEOF_N(name_start, "a", name_len) )
 1333                 mechtype = SPF_mechtype_find(MECH_A);
 1334             else if ( STREQ_SIZEOF_N(name_start, "mx", name_len) )
 1335                 mechtype = SPF_mechtype_find(MECH_MX);
 1336             else if ( STREQ_SIZEOF_N(name_start, "ptr", name_len) )
 1337                 mechtype = SPF_mechtype_find(MECH_PTR);
 1338             else if ( STREQ_SIZEOF_N(name_start, "include", name_len) )
 1339                 mechtype = SPF_mechtype_find(MECH_INCLUDE);
 1340             else if ( STREQ_SIZEOF_N(name_start, "ip4", name_len) )
 1341                 mechtype = SPF_mechtype_find(MECH_IP4);
 1342             else if ( STREQ_SIZEOF_N(name_start, "ip6", name_len) )
 1343                 mechtype = SPF_mechtype_find(MECH_IP6);
 1344             else if ( STREQ_SIZEOF_N(name_start, "exists", name_len) )
 1345                 mechtype = SPF_mechtype_find(MECH_EXISTS);
 1346             else if ( STREQ_SIZEOF_N(name_start, "all", name_len) )
 1347                 mechtype = SPF_mechtype_find(MECH_ALL);
 1348 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
 1349             else if ( STREQ_SIZEOF_N(name_start,
 1350                                             "default=allow", name_len) )
 1351             {
 1352                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
 1353                                 NULL, name_start,
 1354                                 "Deprecated option 'default=allow'");
 1355                 mechtype = SPF_mechtype_find(MECH_ALL);
 1356                 prefix = PREFIX_PASS;
 1357             }
 1358             else if (STREQ_SIZEOF_N(name_start,
 1359                                             "default=softfail",name_len))
 1360             {
 1361                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
 1362                                 NULL, name_start,
 1363                                 "Deprecated option 'default=softfail'");
 1364                 mechtype = SPF_mechtype_find(MECH_ALL);
 1365                 prefix = PREFIX_SOFTFAIL;
 1366             }
 1367             else if ( STREQ_SIZEOF_N(name_start,
 1368                                             "default=deny", name_len) )
 1369             {
 1370                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
 1371                                 NULL, name_start,
 1372                                 "Deprecated option 'default=deny'");
 1373                 mechtype = SPF_mechtype_find(MECH_ALL);
 1374                 prefix = PREFIX_FAIL;
 1375             }
 1376             else if ( STREQ_SIZEOF(name_start, "default=") )
 1377             {
 1378                 SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_OPT,
 1379                                 NULL, name_start,
 1380                                 "Invalid modifier 'default=...'");
 1381                 p = val_end;
 1382                 continue;
 1383             }
 1384 #endif
 1385             /* FIXME  the redirect mechanism needs to be moved to
 1386              * the very end */
 1387             else if ( STREQ_SIZEOF_N(name_start, "redirect", name_len) )
 1388                 mechtype = SPF_mechtype_find(MECH_REDIRECT);
 1389             else
 1390             {
 1391                 SPF_response_add_error_ptr(spf_response, SPF_E_UNKNOWN_MECH,
 1392                                 NULL, name_start,
 1393                                 "Unknown mechanism found");
 1394                 p = val_end;
 1395                 continue;
 1396             }
 1397 
 1398             if (mechtype == NULL) {
 1399                 return SPF_response_add_error_ptr(spf_response,
 1400                                 SPF_E_INTERNAL_ERROR,
 1401                                 NULL, name_start,
 1402                                 "Failed to find specification for "
 1403                                 "a recognised mechanism");
 1404             }
 1405 
 1406             if (spf_server->debug)
 1407                 SPF_debugf("Adding mechanism type %d",
 1408                                 (int)mechtype->mech_type);
 1409 
 1410             val_start = p;
 1411             err = SPF_c_mech_add(spf_server,
 1412                             spf_record, spf_response,
 1413                             mechtype, prefix, &val_start);
 1414             if (err == SPF_E_NO_MEMORY)
 1415                 return err;
 1416             /* XXX Else do nothing. Continue for the next error. */
 1417             /* We shouldn't have to worry about the child function
 1418              * updating the pointer. So we just use our 'well known'
 1419              * copy. */
 1420             p = val_end;
 1421             break;
 1422 
 1423         case '=':
 1424             
 1425             /*
 1426              * parse the modifier
 1427              */
 1428 
 1429             /* modifiers can't have prefixes */
 1430             if (prefix != PREFIX_UNKNOWN)
 1431                 SPF_response_add_error_ptr(spf_response, SPF_E_MOD_W_PREF,
 1432                                 NULL, name_start,
 1433                                 "Modifiers may not have prefixes");
 1434             prefix = PREFIX_UNKNOWN;    /* For redirect/include */
 1435 
 1436 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
 1437             /* Deal with legacy special case */
 1438             if ( STREQ_SIZEOF(name_start, "default=") ) {
 1439                 /* Consider the whole 'default=foo' as a token. */
 1440                 p = val_end;
 1441                 name_len = p - name_start;
 1442                 goto compile_mech;
 1443             }
 1444 #endif
 1445 
 1446             /* We treat 'redirect' as a mechanism. */
 1447             if ( STREQ_SIZEOF(name_start, "redirect=") )
 1448                 goto compile_mech;
 1449 
 1450             p++;
 1451             val_start = p;
 1452             err = SPF_c_mod_add(spf_server,
 1453                             spf_record, spf_response,
 1454                             name_start, name_len, &val_start);
 1455             if (err == SPF_E_NO_MEMORY)
 1456                 return err;
 1457             /* XXX Else do nothing. Continue for the next error. */
 1458             p = val_end;
 1459             break;
 1460             
 1461             
 1462         default:
 1463             SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CHAR,
 1464                             NULL, p,
 1465                             "Invalid character in middle of mechanism");
 1466             p = val_end;
 1467             break;
 1468         }
 1469     }
 1470     
 1471 
 1472     /*
 1473      * check for common mistakes
 1474      */
 1475     SPF_record_lint(spf_server, spf_response, spf_record);
 1476 
 1477 
 1478     /*
 1479      * do final cleanup on the record
 1480      */
 1481 
 1482     /* FIXME realloc (shrink) spfi buffers? */
 1483 
 1484     if (SPF_response_errors(spf_response) > 0) {
 1485         for (i = 0; i < SPF_response_messages(spf_response); i++) {
 1486             spf_error = SPF_response_message(spf_response, i);
 1487             if (SPF_error_errorp(spf_error))
 1488                 return SPF_error_code(spf_error);
 1489         }
 1490         return SPF_response_add_error(spf_response,
 1491                         SPF_E_INTERNAL_ERROR,
 1492                         "Response has errors but can't find one!");
 1493     }
 1494 
 1495     return SPF_E_SUCCESS;
 1496 }
 1497 
 1498 SPF_errcode_t
 1499 SPF_record_compile_macro(SPF_server_t *spf_server,
 1500                                 SPF_response_t *spf_response, 
 1501                                 SPF_macro_t **spf_macrop,
 1502                                 const char *record)
 1503 {
 1504 ALIGN_DECL(
 1505     char             buf[sizeof(SPF_macro_t) + SPF_MAX_MOD_LEN];
 1506 );
 1507     SPF_macro_t     *spf_macro = (SPF_macro_t *)ALIGNED_DECL(buf);
 1508     SPF_data_t      *data;
 1509     SPF_errcode_t    err;
 1510     size_t           size;
 1511     
 1512     data = SPF_macro_data(spf_macro);
 1513     spf_macro->macro_len = 0;
 1514 
 1515     err = SPF_c_parse_macro(spf_server, spf_response,
 1516                     data, &spf_macro->macro_len, SPF_MAX_MOD_LEN,
 1517                     record, strlen(record),
 1518                     SPF_E_BIG_MOD, TRUE);
 1519     if (err != SPF_E_SUCCESS)
 1520         return err;
 1521 
 1522     /* XXX TODO: Tidy this up? */
 1523     size = sizeof(SPF_macro_t) + spf_macro->macro_len;
 1524     *spf_macrop = (SPF_macro_t *)malloc(size);
 1525     if (!*spf_macrop)
 1526         return SPF_E_NO_MEMORY;
 1527     memcpy(*spf_macrop, ALIGNED_DECL(buf), size);
 1528 
 1529     return SPF_E_SUCCESS;
 1530 }