spf_compile.c

Go to the documentation of this file.
00001 /* 
00002  * This program is free software; you can redistribute it and/or modify
00003  * it under the terms of either:
00004  * 
00005  *   a) The GNU Lesser General Public License as published by the Free
00006  *        Software Foundation; either version 2.1, or (at your option) any
00007  *        later version,
00008  * 
00009  *   OR
00010  * 
00011  *   b) The two-clause BSD license.
00012  *
00013  * These licenses can be found with the distribution in the file LICENSES
00014  */
00015 
00016 #include "spf_sys_config.h"
00017 #include "spf_internal.h"
00018 
00019 
00020 #ifdef STDC_HEADERS
00021 # include <stdio.h>             /* stdin / stdout */
00022 # include <stdlib.h>       /* malloc / free */
00023 # include <ctype.h>             /* isupper / tolower */
00024 #endif
00025 
00026 #ifdef HAVE_INTTYPES_H
00027 #include <inttypes.h>
00028 #endif
00029 
00030 #ifdef HAVE_STRING_H
00031 # include <string.h>       /* strstr / strdup */
00032 #else
00033 # ifdef HAVE_STRINGS_H
00034 #  include <strings.h>     /* strstr / strdup */
00035 # endif
00036 #endif
00037 
00038 
00039 
00040 #undef SPF_ALLOW_DEPRECATED_DEFAULT
00041 
00042 #include "spf.h"
00043 #include "spf_internal.h"
00044 #include "spf_response.h"
00045 #include "spf_record.h"
00046 
00047 typedef
00048 enum SPF_cidr_enum {
00049         CIDR_NONE, CIDR_OPTIONAL, CIDR_ONLY
00050 } SPF_cidr_t;
00051 
00052 typedef
00053 enum SPF_domspec_enum {
00054         DOMSPEC_NONE, DOMSPEC_OPTIONAL, DOMSPEC_REQUIRED
00055 } SPF_domspec_t;
00056 
00062 #define SPF_RECORD_BUFSIZ         4096
00063 
00064 #define ALIGN_DECL(decl) union { double d; long l; decl } __attribute__((aligned(_ALIGN_SZ))) u
00065 #define ALIGNED_DECL(var) u.var
00066 
00067 
00068 
00069 typedef
00070 struct SPF_mechtype_struct
00071 {
00072         unsigned char            mech_type;
00073         unsigned char            is_dns_mech;
00074         SPF_domspec_t            has_domainspec;
00075         SPF_cidr_t                       has_cidr;
00076 } SPF_mechtype_t;
00077 
00078 static const SPF_mechtype_t spf_mechtypes[] = {
00079         { MECH_UNKNOWN,         FALSE,          DOMSPEC_NONE,           CIDR_NONE },
00080         { MECH_A,                       TRUE,           DOMSPEC_OPTIONAL,       CIDR_OPTIONAL },
00081         { MECH_MX,                      TRUE,           DOMSPEC_OPTIONAL,       CIDR_OPTIONAL },
00082         { MECH_PTR,                     TRUE,           DOMSPEC_OPTIONAL,       CIDR_NONE },
00083         { MECH_INCLUDE,         TRUE,           DOMSPEC_REQUIRED,       CIDR_NONE },
00084         { MECH_IP4,                     FALSE,          DOMSPEC_REQUIRED,       CIDR_OPTIONAL },
00085         { MECH_IP6,                     FALSE,          DOMSPEC_REQUIRED,       CIDR_OPTIONAL },
00086         { MECH_EXISTS,          TRUE,           DOMSPEC_REQUIRED,       CIDR_NONE },
00087         { MECH_ALL,                     FALSE,          DOMSPEC_NONE,           CIDR_NONE },
00088         { MECH_REDIRECT,        TRUE,           DOMSPEC_REQUIRED,       CIDR_NONE },
00089 };
00090 
00091 #define spf_num_mechanisms \
00092                 sizeof(spf_mechtypes) / sizeof(spf_mechtypes[0])
00093 
00094 static const SPF_mechtype_t *
00095 SPF_mechtype_find(int mech_type)
00096 {
00097         size_t           i;
00098         for (i = 0; i < spf_num_mechanisms; i++) {
00099                 if (spf_mechtypes[i].mech_type == mech_type)
00100                         return &spf_mechtypes[i];
00101         }
00102         return NULL;
00103 }
00104 
00105 __attribute__((warn_unused_result))
00106 static int
00107 SPF_c_ensure_capacity(void **datap, size_t *sizep, size_t length)
00108 {
00109         size_t           size = *sizep;
00110         if (length > size)
00111                 size = length + (length / 4);
00112         if (size > *sizep) {
00113                 void    *tmp = realloc(*datap, size);
00114                 if (!tmp)
00115                         return -1;
00116                 // memset(tmp + *sizep, 'C', (size - *sizep));
00117                 *datap = tmp;
00118                 *sizep = size;
00119         }
00120         return 0;
00121 }
00122 
00132 static SPF_errcode_t
00133 SPF_c_parse_cidr_ip6(SPF_response_t *spf_response,
00134                                 unsigned char *maskp,
00135                                 const char *src)
00136 {
00137         int              mask;
00138 
00139         /*
00140         if (spf_server->debug > 2)
00141                 SPF_debugf("Parsing ip6 CIDR starting at %s", src);
00142         */
00143 
00144         mask = strtoul(src + 1, NULL, 10);
00145 
00146         if (mask > 128) {
00147                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
00148                                                 NULL, src,
00149                                                 "Invalid IPv6 CIDR netmask (>128)");
00150         }
00151         else if (mask == 0) {
00152                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
00153                                                 NULL, src,
00154                                                 "Invalid IPv6 CIDR netmask (=0)");
00155         }
00156         else if (mask == 128) {
00157                 mask = 0;
00158         }
00159 
00160         *maskp = mask;
00161 
00162         return SPF_E_SUCCESS;
00163 }
00164 
00174 static SPF_errcode_t
00175 SPF_c_parse_cidr_ip4(SPF_response_t *spf_response,
00176                                 unsigned char *maskp,
00177                                 const char *src)
00178 {
00179         int              mask;
00180 
00181         /*
00182         if (spf_server->debug > 2)
00183                 SPF_debugf("Parsing ip4 CIDR starting at %s", src);
00184         */
00185 
00186         mask = strtoul(src + 1, NULL, 10);
00187 
00188         if ( mask > 32 ) {
00189                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
00190                                                 NULL, src,
00191                                                 "Invalid IPv4 CIDR netmask (>32)");
00192         }
00193         else if ( mask == 0 ) {
00194                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CIDR,
00195                                                 NULL, src,
00196                                                 "Invalid IPv4 CIDR netmask (=0)");
00197         }
00198         else if ( mask == 32 ) {
00199                 mask = 0;
00200         }
00201 
00202         *maskp = mask;
00203 
00204         return SPF_E_SUCCESS;
00205 }
00206 
00212 static SPF_errcode_t
00213 SPF_c_parse_cidr(SPF_response_t *spf_response,
00214                                 SPF_data_cidr_t *data,
00215                                 const char *src, size_t *src_len)
00216 {
00217         SPF_errcode_t    err;
00218         size_t                   idx;
00219 
00220         memset(data, 0, sizeof(SPF_data_cidr_t));
00221         data->parm_type = PARM_CIDR;
00222 
00223         /* Find the beginning of the CIDR length notation.
00224          * XXX This assumes that there is a non-digit in the string.
00225          * This is always true for SPF records with domainspecs, since
00226          * there has to be an = or a : before it. */
00227         idx = *src_len - 1;
00228         while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
00229                 idx--;
00230 
00231         /* Something is frying my brain and I can't pull an invariant
00232          * out of this suitable for resetting *endp. So I nested the
00233          * 'if's instead. Perhaps I'll manage to refactor later. */
00234 
00235         /* If we have a slash which isn't the last character. */
00236         if (idx < (*src_len - 1) && src[idx] == '/') {
00237                 if (idx > 0 && src[idx - 1] == '/') {
00238                         /* get IPv6 CIDR length */
00239                         err = SPF_c_parse_cidr_ip6(spf_response, &data->ipv6, &src[idx]);
00240                         if (err)
00241                                 return err;
00242                         /* now back up and see if there is a ipv4 cidr length */
00243                         *src_len = idx - 1;     /* The index of the first '/' */
00244                         idx = *src_len - 1;     /* Last character of what is before. */
00245                         while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
00246                                 idx--;
00247 
00248                         /* get IPv4 CIDR length */
00249                         if (idx < (*src_len - 1) && src[idx] == '/') {
00250                                 /* - we know that strtoul terminates on the
00251                                  * '/' so we don't need to null-terminate the
00252                                  * input string. */
00253                                 err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
00254                                 if (err)
00255                                         return err;
00256                                 *src_len = idx;
00257                         }
00258                 }
00259                 else {
00260                         /* get IPv4 CIDR length */
00261                         err = SPF_c_parse_cidr_ip4(spf_response, &data->ipv4, &src[idx]);
00262                         if (err)
00263                                 return err;
00264                         *src_len = idx;
00265                 }
00266         }
00267 
00268         return SPF_E_SUCCESS;
00269 }
00270 
00271 static SPF_errcode_t
00272 SPF_c_parse_var(SPF_response_t *spf_response, SPF_data_var_t *data,
00273                                 const char *src, int is_mod)
00274 {
00275         const char              *token;
00276         const char              *p;
00277         char                     c;
00278         int                              val;
00279 
00280         memset(data, 0, sizeof(SPF_data_var_t));
00281 
00282         p = src;
00283 
00284         /* URL encoding */
00285         c = *p;
00286         if ( isupper( (unsigned char)( c ) ) )
00287         {
00288                 data->url_encode = TRUE;
00289                 c = tolower(c);
00290         }
00291         else
00292                 data->url_encode = FALSE;
00293 
00294 #define SPF_CHECK_IN_MODIFIER() \
00295                 if ( !is_mod ) \
00296                         return SPF_response_add_error_ptr(spf_response, \
00297                                                 SPF_E_INVALID_VAR, NULL, p, \
00298                                                 "'%c' macro is only valid in modifiers", c);
00299 
00300         switch ( c )
00301         {
00302         case 'l':                               /* local-part of envelope-sender */
00303                 data->parm_type = PARM_LP_FROM;
00304                 break;
00305 
00306         case 's':                               /* envelope-sender                              */
00307                 data->parm_type = PARM_ENV_FROM;
00308                 break;
00309 
00310         case 'o':                               /* envelope-domain                              */
00311                 data->parm_type = PARM_DP_FROM;
00312                 break;
00313 
00314         case 'd':                               /* current-domain                               */
00315                 data->parm_type = PARM_CUR_DOM;
00316                 break;
00317 
00318         case 'i':                               /* SMTP client IP                               */
00319                 data->parm_type = PARM_CLIENT_IP;
00320                 break;
00321 
00322         case 'c':                               /* SMTP client IP (pretty)              */
00323                 SPF_CHECK_IN_MODIFIER();
00324                 data->parm_type = PARM_CLIENT_IP_P;
00325                 break;
00326 
00327         case 't':                               /* time in UTC epoch secs               */
00328                 SPF_CHECK_IN_MODIFIER();
00329                 data->parm_type = PARM_TIME;
00330                 break;
00331 
00332         case 'p':                               /* SMTP client domain name              */
00333                 data->parm_type = PARM_CLIENT_DOM;
00334                 break;
00335 
00336         case 'v':                               /* IP ver str - in-addr/ip6             */
00337                 data->parm_type = PARM_CLIENT_VER;
00338                 break;
00339 
00340         case 'h':                               /* HELO/EHLO domain                             */
00341                 data->parm_type = PARM_HELO_DOM;
00342                 break;
00343 
00344         case 'r':                               /* receiving domain                             */
00345                 SPF_CHECK_IN_MODIFIER();
00346                 data->parm_type = PARM_REC_DOM;
00347                 break;
00348 
00349         default:
00350                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_VAR,
00351                                                 NULL, p,
00352                                                 "Unknown variable '%c'", c);
00353         }
00354         p++;
00355         token = p;
00356                 
00357         /* get the number of subdomains to truncate to */
00358         val = 0;
00359         while ( isdigit( (unsigned char)( *p ) ) )
00360         {
00361                 val *= 10;
00362                 val += *p - '0';
00363                 p++;
00364         }
00365         if ( val > 128  ||  (val <= 0 && p != token) )
00366                 return SPF_response_add_error_ptr(spf_response, SPF_E_BIG_SUBDOM,
00367                                                 NULL, token,
00368                                                 "Subdomain truncation depth too large");
00369         data->num_rhs = val;
00370         token = p;
00371                 
00372         /* should the string be reversed? */
00373         if ( *p == 'r' )
00374         {
00375                 data->rev = 1;
00376                 p++;
00377         }
00378         else
00379                 data->rev = FALSE;
00380         token = p;
00381 
00382 
00383         /* check for delimiters */
00384         data->delim_dot = FALSE;
00385         data->delim_dash = FALSE;
00386         data->delim_plus = FALSE;
00387         data->delim_equal = FALSE;
00388         data->delim_bar = FALSE;
00389         data->delim_under = FALSE;
00390 
00391         /*vi:{*/
00392         if ( *p == '}' )
00393                 data->delim_dot = TRUE;
00394 
00395         /*vi:{*/
00396         while( *p != '}' )
00397         {
00398                 token = p;
00399                 switch( *p )
00400                 {
00401                 case '.':
00402                         data->delim_dot = TRUE;
00403                         break;
00404                                 
00405                 case '-':
00406                         data->delim_dash = TRUE;
00407                         break;
00408                                 
00409                 case '+':
00410                         data->delim_plus = TRUE;
00411                         break;
00412                                 
00413                 case '=':
00414                         data->delim_equal = TRUE;
00415                         break;
00416                                 
00417                 case '|':
00418                         data->delim_bar = TRUE;
00419                         break;
00420                                 
00421                 case '_':
00422                         data->delim_under = TRUE;
00423                         break;
00424 
00425                 default:
00426                         return SPF_response_add_error_ptr(spf_response,
00427                                                         SPF_E_INVALID_DELIM, NULL, p,
00428                                                         "Invalid delimiter '%c'", *p);
00429                 }
00430                 p++;
00431         }
00432         p++;
00433         token = p;
00434 
00435 
00436         return SPF_E_SUCCESS;
00437 }
00438 
00439 
00440                 /* Sorry, Wayne. */
00441 #define SPF_ADD_LEN_TO(_val, _len, _max) do { \
00442                         if ( (_val) + _align_sz(_len) > (_max) ) {                              \
00443                                 return SPF_response_add_error_ptr(spf_response,         \
00444                                         big_err, NULL, src,                                                             \
00445                                         "SPF domainspec too long "                                              \
00446                                         "(%d chars, %d max)",                                                   \
00447                                         (_val) + (_len), _max);                                                 \
00448                         }                                                                                                               \
00449                         (_val) += _align_sz(_len);                                                              \
00450                 } while(0)
00451 
00452 #define SPF_INIT_STRING_LITERAL(_avail) do { \
00453                         data->ds.parm_type = PARM_STRING;                                               \
00454                         data->ds.len = 0;                                                                               \
00455                         /* Magic numbers for x/Nc in gdb. */                                    00456                         data->ds.__unused0 = 0xba; data->ds.__unused1 = 0xbe;   00457                         dst = SPF_data_str( data );                                                             00458                         ds_avail = _avail;                                                                              00459                         ds_len = 0;                                                                                             00460                 } while(0)
00461 
00462 #define SPF_ENSURE_STRING_AVAIL(_len)   do {            \
00463                         if (ds_len + _len > ds_avail)                   \
00464                                 return SPF_response_add_error_ptr(spf_response, \
00465                                                                 SPF_E_BIG_STRING, NULL, src,    \
00466                                                         "String literal fragment too long "     \
00467                                                         "(%d chars, %d max)",                           \
00468                                                         ds_len, ds_avail);                                      \
00469                 } while(0)
00470 
00471 #define SPF_FINI_STRING_LITERAL()               do { \
00472                         if ( ds_len > 0 ) {                                                                             \
00473                                 if ( ds_len > SPF_MAX_STR_LEN ) {                                       \
00474                                         return SPF_response_add_error_ptr(spf_response,         \
00475                                                                         SPF_E_BIG_STRING, NULL, src,    \
00476                                                                 "String literal too long "                      \
00477                                                                 "(%d chars, %d max)",                           \
00478                                                                 ds_len, SPF_MAX_STR_LEN);                       \
00479                                 }                                                                                                       \
00480                                 data->ds.len = ds_len;                                                          \
00481                                 len = sizeof( *data ) + ds_len;                                         \
00482                                 SPF_ADD_LEN_TO(*data_used, len, data_avail);            \
00483                                 data = SPF_data_next( data );                                           \
00484                                 ds_len = 0;                                                                                     \
00485                         }                                                                                                               \
00486                 } while(0)
00487 
00505 static SPF_errcode_t
00506 SPF_c_parse_macro(SPF_server_t *spf_server,
00507                                 SPF_response_t *spf_response,
00508                                 SPF_data_t *data, size_t *data_used, size_t data_avail,
00509                                 const char *src, size_t src_len,
00510                                 SPF_errcode_t big_err,
00511                                 int is_mod)
00512 {
00513         SPF_errcode_t            err;
00514                         /* Generic parsing iterators and boundaries */
00515         size_t                           idx;
00516         size_t                           len;
00517                         /* For parsing strings. */
00518         char                            *dst;
00519         size_t                           ds_avail;
00520         size_t                           ds_len;
00521 
00522         if (spf_server->debug)
00523                 SPF_debugf("Parsing macro starting at %s", src);
00524 
00525 #if 0
00526         if ((void *)data != _align_ptr((void *)data))
00527                 SPF_errorf("Data pointer %p is not aligned: Cannot compile.",
00528                 data);
00529 #endif
00530 
00531         /*
00532          * Create the data blocks
00533          */
00534         idx = 0;
00535 
00536         /* Initialise the block as a string. If ds_len == 0 later, we
00537          * will just clobber it. */
00538         SPF_INIT_STRING_LITERAL(data_avail - *data_used);
00539 
00540         // while ( p != end ) {
00541         while (idx < src_len) {
00542                 if (spf_server->debug > 3)
00543                         SPF_debugf("Current data is at %p", data);
00544                 /* Either the unit is terminated by a space, or we hit a %.
00545                  * We should only hit a space if we run past src_len. */
00546                 len = strcspn(&src[idx], " %"); // XXX Also tab?
00547                 if (len > 0) {                          /* An optimisation */
00548                         /* Don't over-run into the CIDR. */
00549                         if (idx + len > src_len)
00550                                 len = src_len - idx;
00551                         if (spf_server->debug > 3)
00552                                 SPF_debugf("Adding string literal (%lu): '%*.*s'",
00553                                                                 (unsigned long)len,
00554                                                                 (int)len, (int)len, &src[idx]);
00555                         /* XXX Bounds-check here. */
00556                         SPF_ENSURE_STRING_AVAIL(len);
00557                         memcpy(dst, &src[idx], len);
00558                         ds_len += len;
00559                         dst += len;
00560                         idx += len;
00561 
00562                         /* If len == 0 then we never entered the while(). Thus
00563                          * if idx == src_len, then len != 0 and we reach this test.
00564                          */
00565                 }
00566                 /* However, this logic is overcomplex and I am a simpleton,
00567                  * so I have moved it out of the condition above. */
00568                 if (idx == src_len)
00569                         break;
00570 
00571                 /* Now, we must have a %-escape code, since if we hit a
00572                  * space, then we are at the end.
00573                  * Incrementing idx consumes the % we hit first, and then
00574                  * we switch on the following character, which also
00575                  * increments idx. */
00576                 idx++;
00577                 switch (src[idx]) {
00578                 case '%':
00579                         if (spf_server->debug > 3)
00580                                 SPF_debugf("Adding literal %%");
00581                         SPF_ENSURE_STRING_AVAIL(1);
00582                         *dst++ = '%';
00583                         ds_len++;
00584                         idx++;
00585                         break;
00586                         
00587                 case '_':
00588                         if (spf_server->debug > 3)
00589                                 SPF_debugf("Adding literal space");
00590                         SPF_ENSURE_STRING_AVAIL(1);
00591                         *dst++ = ' ';
00592                         ds_len++;
00593                         idx++;
00594                         break;
00595 
00596                 case '-':
00597                         if (spf_server->debug > 3)
00598                                 SPF_debugf("Adding escaped space");
00599                         SPF_ENSURE_STRING_AVAIL(3);
00600                         *dst++ = '%'; *dst++ = '2'; *dst++ = '0';
00601                         ds_len += 3;
00602                         idx++;
00603                         break;
00604 
00605                 default:
00606                         if (spf_server->debug > 3)
00607                                 SPF_debugf("Adding illegal %%-follower '%c' at %d",
00608                                 src[idx], idx);
00609                         /* SPF spec says to treat it as a literal, not
00610                          * SPF_E_INVALID_ESC */
00611                         /* FIXME   issue a warning? */
00612                         SPF_ENSURE_STRING_AVAIL(1);
00613                         *dst++ = '%';
00614                         ds_len++;
00615                         break;
00616 
00617                 case '{':  /*vi:}*/
00618                         SPF_FINI_STRING_LITERAL();
00619                         if (spf_server->debug > 3)
00620                                 SPF_debugf("Adding macro, data is at %p", data);
00621 
00622                         /* this must be a variable */
00623                         idx++;
00624                         err = SPF_c_parse_var(spf_response, &data->dv, &src[idx], is_mod);
00625                         if (err != SPF_E_SUCCESS)
00626                                 return err;
00627                         idx += strcspn(&src[idx], "} ");
00628                         if (src[idx] == '}')
00629                                 idx++;
00630                         else if (src[idx] == ' ')
00631                                 return SPF_response_add_error_ptr(spf_response,
00632                                                 SPF_E_INVALID_VAR,
00633                                                 src, &src[idx],
00634                                                 "Unterminated variable?");
00635 
00636 
00637                         len = SPF_data_len(data);
00638                         SPF_ADD_LEN_TO(*data_used, len, data_avail);
00639                         data = SPF_data_next( data );
00640                         if (spf_server->debug > 3)
00641                                 SPF_debugf("Next data is at %p", data);
00642 
00643                         SPF_INIT_STRING_LITERAL(data_avail - *data_used);
00644 
00645                         break;
00646                 }
00647         }
00648 
00649         SPF_FINI_STRING_LITERAL();
00650 
00651         return SPF_E_SUCCESS;
00652 
00653 }
00654 
00655 /* What a fuck-ugly prototype. */
00670 static SPF_errcode_t
00671 SPF_c_parse_domainspec(SPF_server_t *spf_server,
00672                                 SPF_response_t *spf_response,
00673                                 SPF_data_t *data, size_t *data_used, size_t data_avail,
00674                                 const char *src, size_t src_len,
00675                                 SPF_errcode_t big_err,
00676                                 SPF_cidr_t cidr_ok, int is_mod)
00677 {
00678         SPF_errcode_t            err;
00679                         /* Generic parsing iterators and boundaries */
00680         size_t                          len;
00681 
00682         if (spf_server->debug)
00683                 SPF_debugf("Parsing domainspec starting at %s, cidr is %s",
00684                                                 src,
00685                                                 cidr_ok == CIDR_OPTIONAL ? "optional" :
00686                                                 cidr_ok == CIDR_ONLY ? "only" :
00687                                                 cidr_ok == CIDR_NONE ? "forbidden" :
00688                                                 "ERROR!"
00689                                                 );
00690 
00691         /*
00692          * create the CIDR length info
00693          */
00694         if (cidr_ok == CIDR_OPTIONAL || cidr_ok == CIDR_ONLY) {
00695                 err = SPF_c_parse_cidr(spf_response, &data->dc, src, &src_len);
00696                 if (err != SPF_E_SUCCESS)
00697                         return err;
00698                 if (data->dc.ipv4 != 0  ||  data->dc.ipv6 != 0) {
00699                         len = SPF_data_len(data);
00700                         SPF_ADD_LEN_TO(*data_used, len, data_avail);
00701                         data = SPF_data_next(data);
00702                 }
00703 
00704                 if (cidr_ok == CIDR_ONLY && src_len > 0) {
00705                         /* We had a mechanism followed by a '/', thus it HAS to be
00706                          * a CIDR, and the peculiar-looking error message is
00707                          * justified. However, we don't know _which_ CIDR. */
00708                         return SPF_response_add_error_ptr(spf_response,
00709                                                         SPF_E_INVALID_CIDR,
00710                                                         NULL, src,
00711                                                         "Invalid CIDR after mechanism");
00712                 }
00713         }
00714 
00715         return SPF_c_parse_macro(spf_server, spf_response,
00716                         data, data_used, data_avail,
00717                         src, src_len, big_err, is_mod);
00718 }
00719 
00720 
00726 static SPF_errcode_t
00727 SPF_c_parse_ip4(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
00728 {
00729         const char                      *end;
00730         const char                      *p;
00731 
00732         char                             buf[ INET_ADDRSTRLEN ];
00733         size_t                           len;
00734         SPF_errcode_t            err;
00735 
00736         unsigned char            mask;
00737         struct in_addr          *addr;
00738 
00739         start++;
00740         len = strcspn(start, " ");
00741         end = start + len;
00742         p = end - 1;
00743 
00744         mask = 0;
00745         while (isdigit( (unsigned char)(*p) ))
00746                 p--;
00747         if (p != (end - 1) && *p == '/') {
00748                 err = SPF_c_parse_cidr_ip4(spf_response, &mask, p);
00749                 if (err)
00750                         return err;
00751                 end = p;
00752         }
00753         mech->mech_len = mask;
00754 
00755         len = end - start;
00756         if ( len > sizeof( buf ) - 1 )
00757                 return SPF_E_INVALID_IP4;
00758 
00759         memcpy( buf, start, len );
00760         buf[ len ] = '\0';
00761         addr = SPF_mech_ip4_data(mech);
00762         err = inet_pton( AF_INET, buf, addr );
00763         if ( err <= 0 )
00764                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP4,
00765                                                 NULL, buf, NULL);
00766 
00767         return SPF_E_SUCCESS;
00768 }
00769 
00775 static SPF_errcode_t
00776 SPF_c_parse_ip6(SPF_response_t *spf_response, SPF_mech_t *mech, char const *start)
00777 {
00778         const char                      *end;
00779         const char                      *p;
00780 
00781         char                             buf[ INET_ADDRSTRLEN ];
00782         size_t                           len;
00783         int                                      err;
00784 
00785         unsigned char            mask;
00786         struct in6_addr         *addr;
00787 
00788         start++;
00789         len = strcspn(start, " ");
00790         end = start + len;
00791         p = end - 1;
00792 
00793         mask = 0;
00794         while (isdigit( (unsigned char)(*p) ))
00795                 p--;
00796         if (p != (end - 1) && *p == '/') {
00797                 err = SPF_c_parse_cidr_ip6(spf_response, &mask, p);
00798                 if (err)
00799                         return err;
00800                 end = p;
00801         }
00802         mech->mech_len = mask;
00803 
00804         len = end - start;
00805         if ( len > sizeof( buf ) - 1 )
00806                 return SPF_E_INVALID_IP6;
00807 
00808         memcpy( buf, start, len );
00809         buf[ len ] = '\0';
00810         addr = SPF_mech_ip6_data(mech);
00811         err = inet_pton( AF_INET6, buf, addr );
00812         if ( err <= 0 )
00813                 return SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_IP6,
00814                                                 NULL, buf, NULL);
00815 
00816         return SPF_E_SUCCESS;
00817 }
00818 
00819 
00820 /* XXX TODO: Make this take (const char *) instead of (const char **)
00821  * because the caller ignores the modified value. */
00822 __attribute__((warn_unused_result))
00823 static SPF_errcode_t
00824 SPF_c_mech_add(SPF_server_t *spf_server,
00825                                 SPF_record_t *spf_record, SPF_response_t *spf_response,
00826                                 const SPF_mechtype_t *mechtype, int prefix,
00827                                 const char **mech_value)
00828 {
00829         /* If this buffer is an irregular size, intel gcc does not align
00830          * it properly, and all hell breaks loose. */
00831 ALIGN_DECL(
00832         char                             buf[SPF_RECORD_BUFSIZ];
00833 );
00834         SPF_mech_t                      *spf_mechanism = (SPF_mech_t *)ALIGNED_DECL(buf);
00835         SPF_data_t                      *data;
00836         size_t                           data_len;
00837         size_t                           len;
00838         size_t                           src_len;
00839 
00840         SPF_errcode_t            err;
00841 
00842         memset(u.buf, 'B', sizeof(u.buf));      /* Poison the buffer. */
00843         memset(spf_mechanism, 0, sizeof(SPF_mech_t));
00844 
00845         if (spf_server->debug)
00846                 SPF_debugf("SPF_c_mech_add: type=%d, value=%s",
00847                                                 mechtype->mech_type, *mech_value);
00848 
00849         spf_mechanism->prefix_type = prefix;
00850         spf_mechanism->mech_type = mechtype->mech_type;
00851         spf_mechanism->mech_len = 0;
00852 
00853         len = sizeof( SPF_mech_t );
00854 
00855         if ( spf_record->mech_len + len > SPF_MAX_MECH_LEN )
00856                 return SPF_E_BIG_MECH;
00857 
00858         data = SPF_mech_data(spf_mechanism);
00859         data_len = 0;
00860 
00861         src_len = strcspn(*mech_value, " ");
00862 
00863         switch (mechtype->mech_type) {
00864                 /* We know the properties of IP4 and IP6. */
00865                         case MECH_IP4:
00866                         if (**mech_value == ':') {
00867                                 err = SPF_c_parse_ip4(spf_response, spf_mechanism, *mech_value);
00868                                 data_len = sizeof(struct in_addr);
00869                         }
00870                         else {
00871                                 err = SPF_E_MISSING_OPT;
00872                                 SPF_response_add_error_ptr(spf_response, err,
00873                                                 NULL, *mech_value,
00874                                                 "Mechanism requires a value.");
00875                         }
00876                         break;
00877 
00878                 case MECH_IP6:
00879                         if (**mech_value == ':') {
00880                                 err = SPF_c_parse_ip6(spf_response, spf_mechanism, *mech_value);
00881                                 data_len = sizeof(struct in6_addr);
00882                         }
00883                         else {
00884                                 err = SPF_E_MISSING_OPT;
00885                                 SPF_response_add_error_ptr(spf_response, err,
00886                                                 NULL, *mech_value,
00887                                                 "Mechanism requires a value.");
00888                         }
00889                         break;
00890 
00891                 default:
00892                         if (**mech_value == ':' || **mech_value == '=') {
00893                                 if (mechtype->has_domainspec == DOMSPEC_NONE) {
00894                                         err = SPF_E_INVALID_OPT;
00895                                         SPF_response_add_error_ptr(spf_response, err,
00896                                                         NULL, *mech_value,
00897                                                         "Mechanism does not permit a value.");
00898                                 }
00899                                 else {
00900                                         (*mech_value)++; src_len--;
00901                                         err = SPF_c_parse_domainspec(spf_server,
00902                                                                         spf_response,
00903                                                                         data, &data_len, SPF_MAX_MECH_LEN,
00904                                                                         *mech_value, src_len,
00905                                                                         SPF_E_BIG_MECH,
00906                                                                         mechtype->has_cidr, FALSE);
00907                                 }
00908                         }
00909                         else if (**mech_value == '/') {
00910                                 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
00911                                         err = SPF_E_MISSING_OPT;
00912                                         SPF_response_add_error_ptr(spf_response, err,
00913                                                         NULL, *mech_value,
00914                                                         "Mechanism requires a value.");
00915                                 }
00916                                 else if (mechtype->has_cidr == CIDR_NONE) {
00917                                         err = SPF_E_INVALID_CIDR;
00918                                         SPF_response_add_error_ptr(spf_response, err,
00919                                                         NULL, *mech_value,
00920                                                         "Mechanism does not permit a CIDR.");
00921                                 }
00922                                 else {
00923                                         err = SPF_c_parse_domainspec(spf_server,
00924                                                                         spf_response,
00925                                                                         data, &data_len, SPF_MAX_MECH_LEN,
00926                                                                         *mech_value, src_len,
00927                                                                         SPF_E_BIG_MECH,
00928                                                                         CIDR_ONLY, FALSE);
00929                                 }
00930                         }
00931                         else if (**mech_value == ' '  ||  **mech_value == '\0') {
00932                                 if (mechtype->has_domainspec == DOMSPEC_REQUIRED) {
00933                                         err = SPF_E_MISSING_OPT;
00934                                         SPF_response_add_error_ptr(spf_response, err,
00935                                                         NULL, *mech_value,
00936                                                         "Mechanism requires a value.");
00937                                 }
00938                                 else {
00939                                         err = SPF_E_SUCCESS;
00940                                 }
00941                         }
00942                         else {
00943                                 err = SPF_E_SYNTAX;
00944                                 SPF_response_add_error_ptr(spf_response, err,
00945                                                 NULL, *mech_value,
00946                                                 "Unknown character '%c' after mechanism.",
00947                                                 **mech_value);
00948                         }
00949 
00950                         /* Does not apply to ip4/ip6 */
00951                         spf_mechanism->mech_len = data_len;
00952                         break;
00953         }
00954 
00955         len += data_len;
00956 
00957         /* Copy the thing in. */
00958         if (err == SPF_E_SUCCESS) {
00959                 if (mechtype->is_dns_mech)
00960                         spf_record->num_dns_mech++;
00961                 if (SPF_c_ensure_capacity((void **)&spf_record->mech_first,
00962                                                         &spf_record->mech_size,
00963                                                         spf_record->mech_len + len) < 0)
00964                         return SPF_response_add_error_ptr(spf_response,
00965                                                         SPF_E_NO_MEMORY,
00966                                                         NULL, NULL,
00967                                                         "Failed to allocate memory for mechanism");
00968                 memcpy( (char *)spf_record->mech_first + spf_record->mech_len,
00969                         spf_mechanism,
00970                         len);
00971                 spf_record->mech_len += len;
00972                 spf_record->num_mech++;
00973         }
00974 
00975         *mech_value += src_len;
00976 
00977         return err;
00978 }
00979 
00980 __attribute__((warn_unused_result))
00981 static SPF_errcode_t
00982 SPF_c_mod_add(SPF_server_t *spf_server,
00983                                 SPF_record_t *spf_record, SPF_response_t *spf_response,
00984                                 const char *mod_name, size_t name_len,
00985                                 const char **mod_value)
00986 {
00987         /* If this buffer is an irregular size, intel gcc does not align
00988          * it properly, and all hell breaks loose. */
00989 ALIGN_DECL(
00990         char                             buf[SPF_RECORD_BUFSIZ];
00991 );
00992         SPF_mod_t                       *spf_modifier = (SPF_mod_t *)u.buf;
00993         SPF_data_t                      *data;
00994         size_t                           data_len;
00995         size_t                           len;
00996         size_t                           src_len;
00997 
00998         SPF_errcode_t            err;
00999 
01000         if (spf_server->debug)
01001                 SPF_debugf("Adding modifier name=%lu@%s, value=%s",
01002                                                 (unsigned long)name_len, mod_name, *mod_value);
01003 
01004         memset(u.buf, 'A', sizeof(u.buf));
01005         memset(spf_modifier, 0, sizeof(SPF_mod_t));
01006 
01007         if ( name_len > SPF_MAX_MOD_LEN )
01008                 return SPF_E_BIG_MOD;
01009 
01010         spf_modifier->name_len = name_len;
01011         spf_modifier->data_len = 0;
01012 
01013         /* So that spf_modifier + len == SPF_mod_data(spf_modifier) */
01014         len = _align_sz(sizeof( SPF_mod_t ) + name_len);
01015 
01016         if ( spf_record->mod_len + len > SPF_MAX_MOD_LEN )
01017                 return SPF_E_BIG_MOD;
01018 
01019         memcpy(SPF_mod_name(spf_modifier), mod_name, name_len);
01020 
01021         data = SPF_mod_data(spf_modifier);
01022         data_len = 0;
01023 
01024         src_len = strcspn(*mod_value, " ");
01025 
01026         err = SPF_c_parse_macro(spf_server,
01027                                         spf_response,
01028                                         data, &data_len, SPF_MAX_MOD_LEN,
01029                                         *mod_value, src_len,
01030                                         SPF_E_BIG_MOD,
01031                                         TRUE );
01032         spf_modifier->data_len = data_len;
01033         len += data_len;
01034 
01035         /* Copy the thing in. */
01036         if (err == SPF_E_SUCCESS) {
01037                 if (SPF_c_ensure_capacity((void **)&spf_record->mod_first,
01038                                                         &spf_record->mod_size,
01039                                                         spf_record->mod_len + len) < 0)
01040                         return SPF_response_add_error_ptr(spf_response,
01041                                                         SPF_E_NO_MEMORY,
01042                                                         NULL, NULL,
01043                                                         "Failed to allocate memory for modifier");
01044                 memcpy( (char *)spf_record->mod_first + spf_record->mod_len,
01045                         spf_modifier,
01046                         len);
01047                 spf_record->mod_len += len;
01048                 spf_record->num_mod++;
01049         }
01050 
01051         return err;
01052 }
01053 
01054 static void
01055 SPF_record_lint(SPF_server_t *spf_server,
01056                                                                 SPF_response_t *spf_response,
01057                                                                 SPF_record_t *spf_record)
01058 {
01059         SPF_data_t              *d, *data_end;
01060 
01061         char            *s;
01062         char            *s_end;
01063 
01064         int                      found_non_ip;
01065         int                      found_valid_tld;
01066         
01067         SPF_mech_t  *mech;
01068         SPF_data_t  *data;
01069         
01070         int                             i;
01071 
01072         /* FIXME  these warnings suck.  Should call SPF_id2str to give more
01073          * context. */
01074 
01075         mech = spf_record->mech_first;
01076         for (i = 0;
01077                                         i < spf_record->num_mech;
01078                                                 i++,
01079                                                 mech = SPF_mech_next( mech ) )
01080         {
01081                 if ( ( mech->mech_type == MECH_ALL
01082                            || mech->mech_type == MECH_REDIRECT )
01083                          && i != spf_record->num_mech - 1 )
01084                 {
01085                         SPF_response_add_warn(spf_response, SPF_E_MECH_AFTER_ALL,
01086                                                         "Mechanisms found after the \"all:\" "
01087                                                         "mechanism will be ignored.");
01088                 }
01089 
01090                 /*
01091                  * if we are dealing with a mechanism, make sure that the data
01092                  * at least looks like a valid host name.
01093                  *
01094                  * note: this routine isn't called to handle ip4: and ip6: and all
01095                  * the other mechanisms require a host name.
01096                  */
01097 
01098                 if ( mech->mech_type == MECH_IP4
01099                          || mech->mech_type == MECH_IP6 )
01100                         continue;
01101 
01102                 data = SPF_mech_data( mech );
01103                 data_end = SPF_mech_end_data( mech );
01104                 if ( data == data_end )
01105                         continue;
01106 
01107                 if ( data->dc.parm_type == PARM_CIDR )
01108                 {
01109                         data = SPF_data_next( data );
01110                         if ( data == data_end )
01111                                 continue;
01112                 }
01113                 
01114 
01115                 found_valid_tld = FALSE;
01116                 found_non_ip = FALSE;
01117 
01118                 for( d = data; d < data_end; d = SPF_data_next( d ) )
01119                 {
01120                         switch( d->dv.parm_type )
01121                         {
01122                         case PARM_CIDR:
01123                                 SPF_error( "Multiple CIDR parameters found" );
01124                                 break;
01125                                 
01126                         case PARM_CLIENT_IP:
01127                         case PARM_CLIENT_IP_P:
01128                         case PARM_LP_FROM:
01129                                 found_valid_tld = FALSE;
01130                                 break;
01131 
01132                         case PARM_STRING:
01133                                 found_valid_tld = FALSE;
01134 
01135                                 s = SPF_data_str( d );
01136                                 s_end = s + d->ds.len;
01137                                 for( ; s < s_end; s++ ) {
01138                                         if ( !isdigit( (unsigned char)( *s ) ) && *s != '.' && *s != ':' )
01139                                                 found_non_ip = TRUE;
01140 
01141                                         if ( *s == '.' ) 
01142                                                 found_valid_tld = TRUE;
01143                                         else if ( !isalpha( (unsigned char)( *s ) ) )
01144                                                 found_valid_tld = FALSE;
01145                                 }
01146                                 break;
01147 
01148                         default:
01149                                 found_non_ip = TRUE;
01150                                 found_valid_tld = TRUE;
01151                         
01152                                 break;
01153                         }
01154                 }
01155 
01156                 if ( !found_valid_tld || !found_non_ip ) {
01157                         if ( !found_non_ip )
01158                                 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_IP,
01159                                                         "Invalid hostname (an IP address?)");
01160                         else if ( !found_valid_tld )
01161                                 SPF_response_add_warn(spf_response, SPF_E_BAD_HOST_TLD,
01162                                                         "Hostname has a missing or invalid TLD");
01163                 }
01164 
01165         }
01166 
01167         /* FIXME check for modifiers that should probably be mechanisms */
01168 }
01169 
01170 
01171 
01179 SPF_errcode_t
01180 SPF_record_compile(SPF_server_t *spf_server,
01181                                                                 SPF_response_t *spf_response, 
01182                                                                 SPF_record_t **spf_recordp,
01183                                                                 const char *record)
01184 {
01185         const SPF_mechtype_t*mechtype;
01186         SPF_record_t            *spf_record;
01187         SPF_error_t                     *spf_error;
01188         SPF_errcode_t            err;
01189         
01190         const char                      *name_start;
01191         int                                      name_len;
01192 
01193         const char                      *val_start;
01194         const char                      *val_end;
01195         
01196         int                                      prefix;
01197 
01198         const char                      *p;
01199         int                                      i;
01200 
01201 
01202         /*
01203          * make sure we were passed valid data to work with
01204          */
01205         SPF_ASSERT_NOTNULL(spf_server);
01206         SPF_ASSERT_NOTNULL(spf_recordp);
01207         SPF_ASSERT_NOTNULL(record);
01208 
01209         if (spf_server->debug)
01210                 SPF_debugf("Compiling record %s", record);
01211 
01212         /*
01213          * and make sure that we will always set *spf_recordp
01214          * just incase we can't find a valid SPF record
01215          */
01216         *spf_recordp = NULL;
01217 
01218         /*
01219          * See if this is record is even an SPF record
01220          */
01221         p = record;
01222 
01223         if (strncasecmp(p, SPF_VER_STR, sizeof(SPF_VER_STR) - 1) != 0)
01224                 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
01225                                                 NULL, p,
01226                                                 "Could not find a valid SPF record");
01227         p += sizeof( SPF_VER_STR ) - 1;
01228 
01229         if ( *p != '\0' && *p != ' ' )
01230                 return SPF_response_add_error_ptr(spf_response, SPF_E_NOT_SPF,
01231                                                 NULL, p,
01232                                                 "Could not find a valid SPF record");
01233 
01234         spf_record = SPF_record_new(spf_server, record);
01235         if (spf_record == NULL) {
01236                 *spf_recordp = NULL;
01237                 return SPF_response_add_error_ptr(spf_response, SPF_E_NO_MEMORY,
01238                                                 NULL, p,
01239                                                 "Failed to allocate an SPF record");
01240         }
01241         spf_record->version = 1;
01242         *spf_recordp = spf_record;
01243 
01244         /*
01245          * parse the SPF record
01246          */
01247         while (*p != '\0') {
01248                 /* TODO WARN: If it's a \n or a \t */
01249                 /* skip to the next token */
01250                 while (*p == ' ')
01251                         p++;
01252 
01253                 if (*p == '\0')
01254                         break;
01255 
01256                 /* see if we have a valid prefix */
01257                 prefix = PREFIX_UNKNOWN;
01258                 switch (*p) {
01259                         case '+':
01260                                 prefix = PREFIX_PASS;
01261                                 p++;
01262                                 break;
01263                                 
01264                         case '-':
01265                                 prefix = PREFIX_FAIL;
01266                                 p++;
01267                                 break;
01268                                 
01269                         case '~':
01270                                 prefix = PREFIX_SOFTFAIL;
01271                                 p++;
01272                                 break;
01273                                 
01274                         case '?':
01275                                 prefix = PREFIX_NEUTRAL;
01276                                 p++;
01277                                 break;
01278 
01279                         default:
01280                                 while (ispunct((unsigned char)(*p))) {
01281                                         SPF_response_add_error_ptr(spf_response,
01282                                                                         SPF_E_INVALID_PREFIX, NULL, p,
01283                                                                         "Invalid prefix '%c'", *p);
01284                                                 p++;
01285                                 }
01286                                 break;
01287                 }
01288 
01289                 name_start = p;
01290                 val_end = name_start + strcspn(p, " ");
01291 
01292                 /* get the mechanism/modifier */
01293                 if ( ! isalpha( (unsigned char)*p ) ) {
01294                         /* We could just bail on this one. */
01295                         SPF_response_add_error_ptr(spf_response,
01296                                                         SPF_E_INVALID_CHAR, NULL, p,
01297                                                         "Invalid character at start of mechanism");
01298                         p += strcspn(p, " ");
01299                         continue;
01300                 }
01301                 while ( isalnum( (unsigned char)*p ) || *p == '_' || *p == '-' )
01302                         p++;
01303 
01304 /* TODO: These or macros like them are used in several places. Merge. */
01305 #define STREQ_SIZEOF(a, b) \
01306                                 (strncasecmp((a), (b), sizeof( (b) ) - 1) == 0)
01307 #define STREQ_SIZEOF_N(a, b, n) \
01308                                 (((n) == sizeof(b) - 1) && (strncasecmp((a),(b),(n)) == 0))
01309 
01310                 /* See if we have a modifier or a prefix */
01311                 name_len = p - name_start;
01312 
01313                 if (spf_server->debug)
01314                         SPF_debugf("Name starts at  %s", name_start);
01315 
01316                 switch ( *p ) 
01317                 {
01318                 case ':':
01319                 case '/':
01320                 case ' ':
01321                 case '\0':
01322                 compile_mech:           /* A bona fide label */
01323                         
01324                         /*
01325                          * parse the mechanism
01326                          */
01327 
01328                         /* mechanisms default to PREFIX_PASS */
01329                         if ( prefix == PREFIX_UNKNOWN )
01330                                 prefix = PREFIX_PASS;
01331 
01332                         if ( STREQ_SIZEOF_N(name_start, "a", name_len) )
01333                                 mechtype = SPF_mechtype_find(MECH_A);
01334                         else if ( STREQ_SIZEOF_N(name_start, "mx", name_len) )
01335                                 mechtype = SPF_mechtype_find(MECH_MX);
01336                         else if ( STREQ_SIZEOF_N(name_start, "ptr", name_len) )
01337                                 mechtype = SPF_mechtype_find(MECH_PTR);
01338                         else if ( STREQ_SIZEOF_N(name_start, "include", name_len) )
01339                                 mechtype = SPF_mechtype_find(MECH_INCLUDE);
01340                         else if ( STREQ_SIZEOF_N(name_start, "ip4", name_len) )
01341                                 mechtype = SPF_mechtype_find(MECH_IP4);
01342                         else if ( STREQ_SIZEOF_N(name_start, "ip6", name_len) )
01343                                 mechtype = SPF_mechtype_find(MECH_IP6);
01344                         else if ( STREQ_SIZEOF_N(name_start, "exists", name_len) )
01345                                 mechtype = SPF_mechtype_find(MECH_EXISTS);
01346                         else if ( STREQ_SIZEOF_N(name_start, "all", name_len) )
01347                                 mechtype = SPF_mechtype_find(MECH_ALL);
01348 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
01349                         else if ( STREQ_SIZEOF_N(name_start,
01350                                                                                         "default=allow", name_len) )
01351                         {
01352                                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
01353                                                                 NULL, name_start,
01354                                                                 "Deprecated option 'default=allow'");
01355                                 mechtype = SPF_mechtype_find(MECH_ALL);
01356                                 prefix = PREFIX_PASS;
01357                         }
01358                         else if (STREQ_SIZEOF_N(name_start,
01359                                                                                         "default=softfail",name_len))
01360                         {
01361                                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
01362                                                                 NULL, name_start,
01363                                                                 "Deprecated option 'default=softfail'");
01364                                 mechtype = SPF_mechtype_find(MECH_ALL);
01365                                 prefix = PREFIX_SOFTFAIL;
01366                         }
01367                         else if ( STREQ_SIZEOF_N(name_start,
01368                                                                                         "default=deny", name_len) )
01369                         {
01370                                 SPF_response_add_warn_ptr(spf_response, SPF_E_INVALID_OPT,
01371                                                                 NULL, name_start,
01372                                                                 "Deprecated option 'default=deny'");
01373                                 mechtype = SPF_mechtype_find(MECH_ALL);
01374                                 prefix = PREFIX_FAIL;
01375                         }
01376                         else if ( STREQ_SIZEOF(name_start, "default=") )
01377                         {
01378                                 SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_OPT,
01379                                                                 NULL, name_start,
01380                                                                 "Invalid modifier 'default=...'");
01381                                 p = val_end;
01382                                 continue;
01383                         }
01384 #endif
01385                         /* FIXME  the redirect mechanism needs to be moved to
01386                          * the very end */
01387                         else if ( STREQ_SIZEOF_N(name_start, "redirect", name_len) )
01388                                 mechtype = SPF_mechtype_find(MECH_REDIRECT);
01389                         else
01390                         {
01391                                 SPF_response_add_error_ptr(spf_response, SPF_E_UNKNOWN_MECH,
01392                                                                 NULL, name_start,
01393                                                                 "Unknown mechanism found");
01394                                 p = val_end;
01395                                 continue;
01396                         }
01397 
01398                         if (mechtype == NULL) {
01399                                 return SPF_response_add_error_ptr(spf_response,
01400                                                                 SPF_E_INTERNAL_ERROR,
01401                                                                 NULL, name_start,
01402                                                                 "Failed to find specification for "
01403                                                                 "a recognised mechanism");
01404                         }
01405 
01406                         if (spf_server->debug)
01407                                 SPF_debugf("Adding mechanism type %d",
01408                                                                 (int)mechtype->mech_type);
01409 
01410                         val_start = p;
01411                         err = SPF_c_mech_add(spf_server,
01412                                                         spf_record, spf_response,
01413                                                         mechtype, prefix, &val_start);
01414                         if (err == SPF_E_NO_MEMORY)
01415                                 return err;
01416                         /* XXX Else do nothing. Continue for the next error. */
01417                         /* We shouldn't have to worry about the child function
01418                          * updating the pointer. So we just use our 'well known'
01419                          * copy. */
01420                         p = val_end;
01421                         break;
01422 
01423                 case '=':
01424                         
01425                         /*
01426                          * parse the modifier
01427                          */
01428 
01429                         /* modifiers can't have prefixes */
01430                         if (prefix != PREFIX_UNKNOWN)
01431                                 SPF_response_add_error_ptr(spf_response, SPF_E_MOD_W_PREF,
01432                                                                 NULL, name_start,
01433                                                                 "Modifiers may not have prefixes");
01434                         prefix = PREFIX_UNKNOWN;        /* For redirect/include */
01435 
01436 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
01437                         /* Deal with legacy special case */
01438                         if ( STREQ_SIZEOF(name_start, "default=") ) {
01439                                 /* Consider the whole 'default=foo' as a token. */
01440                                 p = val_end;
01441                                 name_len = p - name_start;
01442                                 goto compile_mech;
01443                         }
01444 #endif
01445 
01446                         /* We treat 'redirect' as a mechanism. */
01447                         if ( STREQ_SIZEOF(name_start, "redirect=") )
01448                                 goto compile_mech;
01449 
01450                         p++;
01451                         val_start = p;
01452                         err = SPF_c_mod_add(spf_server,
01453                                                         spf_record, spf_response,
01454                                                         name_start, name_len, &val_start);
01455                         if (err == SPF_E_NO_MEMORY)
01456                                 return err;
01457                         /* XXX Else do nothing. Continue for the next error. */
01458                         p = val_end;
01459                         break;
01460                         
01461                         
01462                 default:
01463                         SPF_response_add_error_ptr(spf_response, SPF_E_INVALID_CHAR,
01464                                                         NULL, p,
01465                                                         "Invalid character in middle of mechanism");
01466                         p = val_end;
01467                         break;
01468                 }
01469         }
01470         
01471 
01472         /*
01473          * check for common mistakes
01474          */
01475         SPF_record_lint(spf_server, spf_response, spf_record);
01476 
01477 
01478         /*
01479          * do final cleanup on the record
01480          */
01481 
01482         /* FIXME realloc (shrink) spfi buffers? */
01483 
01484         if (SPF_response_errors(spf_response) > 0) {
01485                 for (i = 0; i < SPF_response_messages(spf_response); i++) {
01486                         spf_error = SPF_response_message(spf_response, i);
01487                         if (SPF_error_errorp(spf_error))
01488                                 return SPF_error_code(spf_error);
01489                 }
01490                 return SPF_response_add_error(spf_response,
01491                                                 SPF_E_INTERNAL_ERROR,
01492                                                 "Response has errors but can't find one!");
01493         }
01494 
01495         return SPF_E_SUCCESS;
01496 }
01497 
01498 SPF_errcode_t
01499 SPF_record_compile_macro(SPF_server_t *spf_server,
01500                                                                 SPF_response_t *spf_response, 
01501                                                                 SPF_macro_t **spf_macrop,
01502                                                                 const char *record)
01503 {
01504 ALIGN_DECL(
01505         char                     buf[sizeof(SPF_macro_t) + SPF_MAX_MOD_LEN];
01506 );
01507         SPF_macro_t             *spf_macro = (SPF_macro_t *)ALIGNED_DECL(buf);
01508         SPF_data_t              *data;
01509         SPF_errcode_t    err;
01510         size_t                   size;
01511         
01512         data = SPF_macro_data(spf_macro);
01513         spf_macro->macro_len = 0;
01514 
01515         err = SPF_c_parse_macro(spf_server, spf_response,
01516                                         data, &spf_macro->macro_len, SPF_MAX_MOD_LEN,
01517                                         record, strlen(record),
01518                                         SPF_E_BIG_MOD, TRUE);
01519         if (err != SPF_E_SUCCESS)
01520                 return err;
01521 
01522         /* XXX TODO: Tidy this up? */
01523         size = sizeof(SPF_macro_t) + spf_macro->macro_len;
01524         *spf_macrop = (SPF_macro_t *)malloc(size);
01525         if (!*spf_macrop)
01526                 return SPF_E_NO_MEMORY;
01527         memcpy(*spf_macrop, ALIGNED_DECL(buf), size);
01528 
01529         return SPF_E_SUCCESS;
01530 }

Generated on Tue Nov 4 13:27:39 2008 for libspf2 by  doxygen 1.5.4