From a80d71e7dc3b46980b9f91c9238599fee26cc1b2 Mon Sep 17 00:00:00 2001
From: John Denker <jsd@av8n.com>
Date: Sat, 24 Nov 2012 11:53:34 -0800
Subject: working toward identifying the "owning domain"

---
 tools/libskrewt.c | 86 ++++++++++++++++++++++++++++++++++++++-----------------
 tools/libskrewt.h | 39 +++++++++++++++++--------
 tools/makefile    |  3 +-
 3 files changed, 89 insertions(+), 39 deletions(-)

(limited to 'tools')

diff --git a/tools/libskrewt.c b/tools/libskrewt.c
index b41d7d0..3c820f1 100644
--- a/tools/libskrewt.c
+++ b/tools/libskrewt.c
@@ -64,7 +64,7 @@ int skrewt::krunch_rfrom(){
         << word << "'" << endl;
     return ex_syserr;
   }
-  parse >> proximta_rDNS;
+  parse >> proximta_rDNS.name;
   for (;;) {            // loop over words in this record
     parse >> word;
     size_t len = word.length();
@@ -75,8 +75,8 @@ int skrewt::krunch_rfrom(){
     }
     if (word == "by") break;
     if (word == "(HELO" /*)*/) {
-      parse >> proximta_HELO;
-      proximta_HELO = rtrim(proximta_HELO, "()");
+      parse >> proximta_HELO.name;
+      proximta_HELO.name = rtrim(proximta_HELO.name, "()");
       continue;
     }
     if (word[0] != '(' || word[len-1] != ')') {
@@ -92,8 +92,8 @@ int skrewt::krunch_rfrom(){
     }
   }
 // provide some kind of default? maybe not.
-  if (0) if (proximta_HELO == "") {
-    proximta_HELO = proximta_rDNS + " (+-)";
+  if (0) if (proximta_HELO.name == "") {
+    proximta_HELO.name = proximta_rDNS.name + " (+-)";
   }
 
   return 0;
@@ -178,7 +178,7 @@ int skrewt::headers(istream& xin){
     } else if (headword == "to") {
       to = rest;
     } else if (headword == "return-path") {
-      return_path = rest;
+      return_path.name = rest;
     } else if (headword == "message-id") {
       message_id = rest;
     }
@@ -214,6 +214,30 @@ int skrewt::dump_bigbuf(std::ostream& xout){
   return 0;
 }
 
+void check_name(name_tester& fqdn, const string ip) {
+   if (ip == "") {
+     cerr << "SPF: should never happen: email with no IP?" <<endl;
+     fqdn.spf = fqdn.map2ip = neutral;
+     return;
+   }
+   sepofra my_spf;
+   try {
+     my_spf.check(ip,
+         fqdn.name,
+         "junk",
+         "morejunk", 0/* verbosity */);
+     cerr << progid << " " << my_spf.explain() << endl;
+// keep a copy of the result:
+     fqdn.spf = neutral;
+     if (my_spf.result == SPF_RESULT_PASS) fqdn.spf = pass;
+     if (my_spf.result == SPF_RESULT_FAIL) fqdn.spf = fail;
+     /* anything else, such as soft_fail, is treated as neutral */
+   } catch (bad_thing foo) {
+     cerr << "Caught bad thing: " << foo.what() << endl;
+     return;
+   }
+}
+
 int skrewt::interstage(){
   if (saw_blank_line) {/* ignore */}
 // Note that the headers are in reverse-chronological order.
@@ -221,36 +245,31 @@ int skrewt::interstage(){
 // at the time skrewt normally runs, since it is upstream
 // of qmail-queue.
   cerr << progid << " Return-path: "
-    << (return_path.length() ? return_path : "[not set yet]") <<endl;
+    << (return_path.name.length() ? return_path.name : "[not set yet]") <<endl;
 
   { // parse the 'Received: from' line:
     cerr << progid << " Received: " << received_from <<endl;
     int rslt = krunch_rfrom();
     if (rslt) return rslt;
-    cerr << progid << " === rDNS:     "  << proximta_rDNS << endl;
-    cerr << progid << " === HELO:     "  << proximta_HELO << endl;
+    if (proximta_AuthUser == "") {
+// FIXME:  also check return-path aka envelope-from
+      check_name(proximta_HELO, proximta_IP);
+      check_name(proximta_rDNS, proximta_IP);
+    }
+
+    cerr << progid << " === rDNS:     "  << proximta_rDNS.name
+                << "  " << decode_test_state[proximta_rDNS.spf]
+                << "  " << decode_test_state[proximta_rDNS.map2ip]
+                << endl;
+    cerr << progid << " === HELO:     "  << proximta_HELO.name
+                << "  " << decode_test_state[proximta_HELO.spf]
+                << "  " << decode_test_state[proximta_HELO.map2ip]
+                << endl;
     cerr << progid << " === IP:       "  << proximta_IP << endl;
     cerr << progid << " === Mid       '" << message_id << "'" << endl;
     cerr << progid << " === AuthUser: "  << proximta_AuthUser << endl;
   }
 
-  if (proximta_AuthUser == "")
-   if (proximta_IP != "") {
-    sepofra my_spf;
-    try {
-      my_spf.check(proximta_IP,
-          proximta_HELO,
-          return_path,
-          "junk", 0/* verbosity */);
-      cerr << progid << " " << my_spf.explain() << endl;
-// keep a copy of the result:
-      spf_result = my_spf.result;
-    } catch (bad_thing foo) {
-      cerr << "Caught bad thing: " << foo.what() << endl;
-      return ex_syserr;
-    }
-  }
-
 // The logic here is:  In order:
 // 1:: If whitelisted, accept.  No greylisting, no spam-checking.
 // 2:: If blacklisted, reject.  No greylisting, no spam-checking.
@@ -408,6 +427,21 @@ void dump(const list<conner> sitch){
   cerr << endl;
 }
 
+// constructor
+
+skrewt::skrewt()
+  : spf_result(SPF_RESULT_INVALID),
+    boundary("x-xx-x"), msgsize(0), saw_blank_line(0), recno(0),
+    maxsize(1000*1000), error_exit(0), mid_required(0),
+    headerbuf(0), bigbuf(0),
+    lookahead(1, "")
+{
+// expand the macro in a way that will initialize the decoder table:
+# define foo(name) decode_test_state[name] = #name;
+  test_state_macro
+# undef foo
+}
+
 int skrewt::body(std::istream& xin, std::ostream& xout){
   list<conner> sitch;
   if (content_type.length()) {
diff --git a/tools/libskrewt.h b/tools/libskrewt.h
index 34cdfd3..2360b15 100644
--- a/tools/libskrewt.h
+++ b/tools/libskrewt.h
@@ -5,6 +5,28 @@
 #include "qq_exit_codes.h"              // a bit of a kludge
 extern std::string progid;
 
+#define test_state_macro \
+foo(untested)  \
+foo(pass)   \
+foo(neutral)    \
+foo(fail)
+
+// expand the codes to make some <const int> names:
+#define foo(name) name,
+typedef enum {
+  test_state_macro
+} test_state;
+#undef foo
+
+class name_tester {
+public:
+  std::string name;          // typically a FQDN
+  test_state spf;
+  test_state map2ip;
+  name_tester() : name(""), spf(untested), map2ip(untested)
+  {}
+};
+
 class xstr {
 public:
   int err;
@@ -16,12 +38,12 @@ public:
 class skrewt{
 public:
   std::string received_from;         // envelope HELO among other things
-    std::string proximta_HELO;
-    std::string proximta_rDNS;
+    name_tester proximta_HELO;
+    name_tester proximta_rDNS;
     std::string proximta_IP;
     std::string proximta_AuthUser;
     SPF_result_t spf_result;
-  std::string return_path;           // envelope MAIL FROM
+  name_tester return_path;           // envelope MAIL FROM
   std::string boundary;
   std::string to;
   std::string from;
@@ -39,16 +61,9 @@ public:
   std::vector<std::string> headerbuf;
   std::vector<std::string> bigbuf;
   xstr lookahead;
+  std::map<test_state,std::string> decode_test_state;
 
-  // constructor
-  skrewt()
-  : spf_result(SPF_RESULT_INVALID),
-    boundary("x-xx-x"), msgsize(0), saw_blank_line(0), recno(0),
-    maxsize(1000*1000), error_exit(0), mid_required(0),
-    headerbuf(0), bigbuf(0),
-    lookahead(1, "")
-  {}
-
+  skrewt();       // constructor
   xstr getRecord(std::istream& xin);
   xstr getLine(std::istream& xin);
   int headers(std::istream& xin);
diff --git a/tools/makefile b/tools/makefile
index 2373125..7d0afbf 100644
--- a/tools/makefile
+++ b/tools/makefile
@@ -29,7 +29,8 @@ qprogs = $(qmain:%.c=%)
 moremain = wripper.c bash-c.c ltgrey.c fixown.c pipette.c
 moreprogs = $(moremain:%.c=%)
 
-nonmain = libltgrey.c
+# sources for libraries
+nonmain = libltgrey.c libskrewt.c
 
 sources = $(qmain) $(moremain) $(nonmain)
 
-- 
cgit v1.2.3