diff options
author | John Denker <jsd@av8n.com> | 2012-07-31 18:08:21 -0700 |
---|---|---|
committer | John Denker <jsd@av8n.com> | 2012-07-31 18:08:21 -0700 |
commit | 159a3f448ad2caf9468921a4387c2dbafe09c3a3 (patch) | |
tree | b16f9593b43e0cada94d6a87c4380fd0ed1d56eb | |
parent | 3f3c46c8ee8ef118ce73da7d3235364edf390cfe (diff) |
bring sepofra over from ~/hack/
-rw-r--r-- | tools/bad_thing.h | 45 | ||||
-rw-r--r-- | tools/makefile | 4 | ||||
-rw-r--r-- | tools/sepofra.c | 178 | ||||
-rw-r--r-- | tools/sepofra.h | 49 |
4 files changed, 274 insertions, 2 deletions
diff --git a/tools/bad_thing.h b/tools/bad_thing.h new file mode 100644 index 0000000..ee988dc --- /dev/null +++ b/tools/bad_thing.h @@ -0,0 +1,45 @@ +#ifndef BAD_THING_H +#define BAD_THING_H +class payloader{ +public: + int refcount; + char str[0]; +}; + +class bad_thing: public std::exception{ + payloader* load; + char* &raw; + +public: + virtual const char* what() const throw() { + return load->str; + } + bad_thing(); +// copy constructor. +// It's OK to copy a pointer to the refcount, +// but it would not be OK to copy the refcount! + bad_thing(const bad_thing &other) + : load(other.load), raw((char*&) load) + { + load->refcount++; + //xx std::cerr << "Copied! --> " << load->refcount << std::endl; + } + + bad_thing(const std::string msg) + : raw((char*&) load) + { + size_t len = msg.size(); + raw = new char[1+len + sizeof(payloader)]; + load->refcount = 1; + for (unsigned int ii = 0; ii < len; ii++){ + load->str[ii] = msg[ii]; + } + load->str[len] = 0; + } + ~bad_thing() throw() { + if (--load->refcount) return; + //xx std::cerr << "delete!" << std::endl; + delete[] raw; + } +}; +#endif diff --git a/tools/makefile b/tools/makefile index 723d756..5bd5711 100644 --- a/tools/makefile +++ b/tools/makefile @@ -47,8 +47,8 @@ fixown2: fixown.o utils.o chmod o-rwx $@ ./fixown $@ -skrewt: skrewt.o utils.o - $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ +skrewt: skrewt.o utils.o sepofra.o + $(CC) $^ -lboost_filesystem-mt -lboost_system -lspf2 -o $@ ./fixown $@ greylist: greylist.o utils.o diff --git a/tools/sepofra.c b/tools/sepofra.c new file mode 100644 index 0000000..5072b36 --- /dev/null +++ b/tools/sepofra.c @@ -0,0 +1,178 @@ +#include <iostream> +#include <sstream> + +#include "sepofra.h" + + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +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; +} + +string subdomain(const string ema){ + size_t where = ema.find('.'); + if (where != ema.npos){ + return ema.substr(1+where); + } + return ema; +} + +SPF_result_t sepofra::check1(const string host, + const string msg, const int debug){ + if (!host.length()) return SPF_RESULT_INVALID; + + 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 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; + mailfrom = opt_mailfrom; + string mailfrom_domain = domain_part(opt_mailfrom); + string auth; + + 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 << "SPF_request_new failed" << endl; + break; + } + + if ( SPF_request_set_ipv4_str( spf_request, opt_ip.c_str() ) ) { + cerr << "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(subdomain(opt_helo), "HELO subdomain", opt_debug); + if (result == SPF_RESULT_PASS) break; + if (result == SPF_RESULT_FAIL) break; + + result = check1(subdomain(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; +} + +string sepofra::explain() const { + if (result == SPF_RESULT_INVALID) return ""; +// now build a string + stringstream build; + string summary = SPF_strresult(result); + if (result != SPF_RESULT_PASS + && result != SPF_RESULT_FAIL) 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<string>::const_iterator ptr = authorities.begin(); + ptr != authorities.end(); ptr++) { + if (didsome++) build << " "; + build << *ptr; + } + build << "}"; + } + /* ( */ build << ")"; + build << " client-ip=" << ip << ";"; + build << " envelope-from=" << mailfrom << ";"; + 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))); + +} diff --git a/tools/sepofra.h b/tools/sepofra.h new file mode 100644 index 0000000..89719fb --- /dev/null +++ b/tools/sepofra.h @@ -0,0 +1,49 @@ +#include <string> +#include <list> +# include <sys/socket.h> /* inet_ functions / structs */ +# include <netinet/in.h> /* inet_ functions / structs */ +# include <arpa/inet.h> /* in_addr struct */ + +#ifdef __cplusplus +extern "C" { +# include "spf2/spf.h" +} +#else +# include "spf2/spf.h" +#endif + +#include <string.h> +#include "bad_thing.h" + +class sepofra{ +public: + SPF_result_t result; + std::list<std::string> authorities; + std::string ip; + std::string mailfrom; + SPF_request_t* spf_request; + SPF_response_t* spf_response; + + sepofra() + : result(SPF_RESULT_INVALID), + spf_request(NULL), + spf_response(NULL) + {} + void check( + const std::string opt_ip, + const std::string opt_helo, + const std::string opt_mailfrom, + const std::string opt_rcpt_to, + const int opt_debug); + + std::string explain() const; + SPF_result_t check1( + const std::string host, + const std::string msg, + const int debug); +}; + + +void dumpit(const int opt_debug, + SPF_response_t* spf_response, + const char* msg); |