#include #include #include /* gethostname */ #include "sepofra.h" #include "utils.h" /* for trim() */ #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 255 #endif ///// Important reference: ///// http://www.ietf.org/rfc/rfc4408.txt ///// using namespace std; string domain_part(const string ema){ size_t where = ema.find('@'); if (where != ema.npos){ return ema.substr(1+where); } return ema; } // strips off one level of domain name, // provided that at least two levels remain. // foo.bar.com --> bar.com // bar.com --> bar.com // ..foo.bar.com --> bar.com string subdomain2plus(const string ema){ string sub(ema); while (sub[0] == '.') sub = sub.substr(1); size_t where = sub.find('.'); if (where == string::npos) return ema; sub = sub.substr(1+where); if (sub.find(".") == string::npos) return ema; return sub; } SPF_result_t sepofra::check1(const string _host, const string msg, const int debug){ string host(_host); size_t where = host.find("@"); if (where != string::npos){ host = host.substr(1+where); } if (!host.length()) return SPF_RESULT_INVALID; if (seen.find(host) != seen.end()) { // already checked this one return seen[host]; } seen[host] = SPF_RESULT_TEMPERROR; authorities.push_back(host); if (SPF_request_set_env_from( spf_request, host.c_str() ) ) { throw bad_thing (("Invalid " + msg + ": " + host).c_str()); } if (spf_response) SPF_response_free(spf_response); SPF_request_query_mailfrom(spf_request, &spf_response); if (debug) dumpit(debug, spf_response, ""); return seen[host] = SPF_response_result(spf_response); } void sepofra::check( const string opt_ip, const string opt_helo, const string opt_mailfrom, const string opt_rcpt_to, const int opt_debug) { SPF_server_t* spf_server = NULL; sepofra rslt; ip = opt_ip; helo = trim(opt_helo, " \t\r\n<.>"); mailfrom = trim(opt_mailfrom, " \t\r\n<.>"); string mailfrom_domain = domain_part(opt_mailfrom); do { spf_server = SPF_server_new(SPF_DNS_CACHE, opt_debug); if (spf_server == NULL) { cerr << "SPF_server_new failed" << endl; break; } spf_request = SPF_request_new(spf_server); if (spf_request == NULL) { cerr << "sepofra::check SPF_request_new failed" << endl; break; } if ( SPF_request_set_ipv4_str( spf_request, opt_ip.c_str() ) && SPF_request_set_ipv6_str( spf_request, opt_ip.c_str() ) ) { cerr << "sepofra::check: Invalid IP address: '" << opt_ip << "'" << endl; break; } result = check1(opt_helo, "HELO domain", opt_debug); if (result == SPF_RESULT_PASS) break; if (result == SPF_RESULT_FAIL) break; result = check1(mailfrom, "MAILFROM domain", opt_debug); if (result == SPF_RESULT_PASS) break; if (result == SPF_RESULT_FAIL) break; result = check1(subdomain2plus(opt_helo), "HELO subdomain", opt_debug); if (result == SPF_RESULT_PASS) break; if (result == SPF_RESULT_FAIL) break; result = check1(subdomain2plus(mailfrom), "MAILFROM domain", opt_debug); if (result == SPF_RESULT_PASS) break; if (result == SPF_RESULT_FAIL) break; } while(0); if (spf_response) SPF_response_free(spf_response); if (spf_request) SPF_request_free(spf_request); if (spf_server) SPF_server_free(spf_server); return; } // return "" if result is invalid // otherwise build a string explaining how SPF reached its conclusion string sepofra::explain() const { if (result == SPF_RESULT_INVALID) return ""; stringstream build; string summary = SPF_strresult(result); if (result != SPF_RESULT_PASS && result != SPF_RESULT_FAIL && result != SPF_RESULT_INVALID) summary = "neutral"; char hostname[1+HOST_NAME_MAX]; gethostname(hostname, 1+HOST_NAME_MAX); build << "Received-SPF: "; build << summary; build << " (" << hostname; /* ) */ build << ": sender " << ip; if (result == SPF_RESULT_PASS){ build << " is approved by {" << authorities.back() << "}"; } else if (result == SPF_RESULT_FAIL){ build << " is forbidden by {" << authorities.back() << "}"; } else { build << " is neither approved nor forbidden by {"; int didsome(0); for (list::const_iterator ptr = authorities.begin(); ptr != authorities.end(); ptr++) { if (didsome++) build << " "; build << *ptr; } build << "}"; } /* ( */ build << ")"; if (ip != "") build << " client-ip=" << ip << ";"; if (mailfrom != "") build << " envelope-from=" << mailfrom << ";"; if (helo != "") build << " helo=" << helo; return build.str(); } void dumpit(const int opt_debug, SPF_response_t* spf_response, const char* msg) { int i; printf("---- %s\n", msg); if ( opt_debug > 0 ) { printf ( "result = %s (%d)\n", SPF_strresult(SPF_response_result(spf_response)), SPF_response_result(spf_response)); printf ( "err = %s (%d)\n", SPF_strerror(SPF_response_errcode(spf_response)), SPF_response_errcode(spf_response)); for (i = 0; i < SPF_response_messages(spf_response); i++) { SPF_error_t *err = SPF_response_message(spf_response, i); printf ( "%s_msg = (%d) %s\n", (SPF_error_errorp(err) ? "warn" : "err"), SPF_error_code(err), SPF_error_message(err)); } } #define VALID_STR(x) (x ? x : "") printf("Bottom line result: %s\n", SPF_strresult( SPF_response_result(spf_response))); printf("Reason: %s\n", SPF_strreason(spf_response->reason)); printf("SMTP comment: %s\n", VALID_STR(SPF_response_get_smtp_comment(spf_response))); printf("Header comment: %s\n", VALID_STR(SPF_response_get_header_comment(spf_response))); printf("Received spf: %s\n", VALID_STR(SPF_response_get_received_spf(spf_response))); }