libspf2 1.2.10
|
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("%s", "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("%s", "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("%s", "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[ INET6_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[ INET6_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 size_t 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 }