00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "spf_sys_config.h"
00017 #include "spf_internal.h"
00018
00019
00020 #ifdef STDC_HEADERS
00021 # include <stdio.h>
00022 # include <stdlib.h>
00023 # include <ctype.h>
00024 #endif
00025
00026 #ifdef HAVE_INTTYPES_H
00027 #include <inttypes.h>
00028 #endif
00029
00030 #ifdef HAVE_STRING_H
00031 # include <string.h>
00032 #else
00033 # ifdef HAVE_STRINGS_H
00034 # include <strings.h>
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
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
00141
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
00183
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
00224
00225
00226
00227 idx = *src_len - 1;
00228 while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
00229 idx--;
00230
00231
00232
00233
00234
00235
00236 if (idx < (*src_len - 1) && src[idx] == '/') {
00237 if (idx > 0 && src[idx - 1] == '/') {
00238
00239 err = SPF_c_parse_cidr_ip6(spf_response, &data->ipv6, &src[idx]);
00240 if (err)
00241 return err;
00242
00243 *src_len = idx - 1;
00244 idx = *src_len - 1;
00245 while (idx > 0 && isdigit( (unsigned char)(src[idx]) ))
00246 idx--;
00247
00248
00249 if (idx < (*src_len - 1) && src[idx] == '/') {
00250
00251
00252
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
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
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':
00303 data->parm_type = PARM_LP_FROM;
00304 break;
00305
00306 case 's':
00307 data->parm_type = PARM_ENV_FROM;
00308 break;
00309
00310 case 'o':
00311 data->parm_type = PARM_DP_FROM;
00312 break;
00313
00314 case 'd':
00315 data->parm_type = PARM_CUR_DOM;
00316 break;
00317
00318 case 'i':
00319 data->parm_type = PARM_CLIENT_IP;
00320 break;
00321
00322 case 'c':
00323 SPF_CHECK_IN_MODIFIER();
00324 data->parm_type = PARM_CLIENT_IP_P;
00325 break;
00326
00327 case 't':
00328 SPF_CHECK_IN_MODIFIER();
00329 data->parm_type = PARM_TIME;
00330 break;
00331
00332 case 'p':
00333 data->parm_type = PARM_CLIENT_DOM;
00334 break;
00335
00336 case 'v':
00337 data->parm_type = PARM_CLIENT_VER;
00338 break;
00339
00340 case 'h':
00341 data->parm_type = PARM_HELO_DOM;
00342 break;
00343
00344 case 'r':
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
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
00373 if ( *p == 'r' )
00374 {
00375 data->rev = 1;
00376 p++;
00377 }
00378 else
00379 data->rev = FALSE;
00380 token = p;
00381
00382
00383
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
00392 if ( *p == '}' )
00393 data->delim_dot = TRUE;
00394
00395
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
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 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
00515 size_t idx;
00516 size_t len;
00517
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
00533
00534 idx = 0;
00535
00536
00537
00538 SPF_INIT_STRING_LITERAL(data_avail - *data_used);
00539
00540
00541 while (idx < src_len) {
00542 if (spf_server->debug > 3)
00543 SPF_debugf("Current data is at %p", data);
00544
00545
00546 len = strcspn(&src[idx], " %");
00547 if (len > 0) {
00548
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
00556 SPF_ENSURE_STRING_AVAIL(len);
00557 memcpy(dst, &src[idx], len);
00558 ds_len += len;
00559 dst += len;
00560 idx += len;
00561
00562
00563
00564
00565 }
00566
00567
00568 if (idx == src_len)
00569 break;
00570
00571
00572
00573
00574
00575
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
00610
00611
00612 SPF_ENSURE_STRING_AVAIL(1);
00613 *dst++ = '%';
00614 ds_len++;
00615 break;
00616
00617 case '{':
00618 SPF_FINI_STRING_LITERAL();
00619 if (spf_server->debug > 3)
00620 SPF_debugf("Adding macro, data is at %p", data);
00621
00622
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
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
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
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
00706
00707
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
00821
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
00830
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));
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
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
00951 spf_mechanism->mech_len = data_len;
00952 break;
00953 }
00954
00955 len += data_len;
00956
00957
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
00988
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
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
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
01073
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
01092
01093
01094
01095
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
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
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
01214
01215
01216 *spf_recordp = NULL;
01217
01218
01219
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
01246
01247 while (*p != '\0') {
01248
01249
01250 while (*p == ' ')
01251 p++;
01252
01253 if (*p == '\0')
01254 break;
01255
01256
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
01293 if ( ! isalpha( (unsigned char)*p ) ) {
01294
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
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
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:
01323
01324
01325
01326
01327
01328
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
01386
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
01417
01418
01419
01420 p = val_end;
01421 break;
01422
01423 case '=':
01424
01425
01426
01427
01428
01429
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;
01435
01436 #ifdef SPF_ALLOW_DEPRECATED_DEFAULT
01437
01438 if ( STREQ_SIZEOF(name_start, "default=") ) {
01439
01440 p = val_end;
01441 name_len = p - name_start;
01442 goto compile_mech;
01443 }
01444 #endif
01445
01446
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
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
01474
01475 SPF_record_lint(spf_server, spf_response, spf_record);
01476
01477
01478
01479
01480
01481
01482
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
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 }