From 60ebe6c00a2868e6bb69ef30cf04e5568276808b Mon Sep 17 00:00:00 2001
From: John Denker <jsd@av8n.com>
Date: Fri, 20 Jul 2012 22:18:31 -0700
Subject: implement penaltybox return-code (exit status) also make sure DNS
 checking doesn't interfere with more basic duties such as keeping the
 greylist database updated

---
 tools/filters.conf  |  6 ++--
 tools/greylist.c    | 82 +++++++++++++++++++++++++++++++++++------------------
 tools/hi-q.c        | 21 ++++++++------
 tools/hi-test2.conf |  2 +-
 4 files changed, 72 insertions(+), 39 deletions(-)

(limited to 'tools')

diff --git a/tools/filters.conf b/tools/filters.conf
index 9342441..f5b35f5 100644
--- a/tools/filters.conf
+++ b/tools/filters.conf
@@ -1,7 +1,7 @@
 # configuration file for hi-q
 series  /var/qmail/bin/skrewt
-stub    /var/qmail/bin/greylist
-sa      /usr/local/bin/spamc -Y 0 -s 1000000
+stub    /var/qmail/bin/greylist -check -v
+sa      /usr/local/bin/spamc -Y 0 -s 1000000 -x
 qq      /var/qmail/bin/qmail-queue
 
-postspam /var/qmail/bin/greylist -suffix (post) -penalize 86400
+postspam /var/qmail/bin/greylist -suffix (post) -penalize 86400 -v
diff --git a/tools/greylist.c b/tools/greylist.c
index 465a78e..c5c891f 100644
--- a/tools/greylist.c
+++ b/tools/greylist.c
@@ -31,6 +31,7 @@ const int probation(4*hour);
 #define foo(name, num) const int ex_ ## name = num
 #define bar foo(good, 0) ;\
 foo(spam, 21) ;\
+foo(penaltybox, 22) ;\
 foo(greylisting, 70) ;\
 foo(syserr, 71) ;\
 foo(comerr, 74) ;
@@ -90,9 +91,12 @@ public:
   int ac_age;
   string suffix;
   string progid;
+  int verbosity;
 
   whatsit(const string name, const string _dirname)
-  : dirname(_dirname), progname(name), mypid(getpid()), mod_age(0), ac_age(0)
+  : dirname(_dirname), progname(name), mypid(getpid()),
+    mod_age(0), ac_age(0),
+    verbosity(0)
   {
     gettimeofday(&now, NULL);
   }
@@ -103,9 +107,15 @@ public:
   void bind();
 };
 
+string basename(const string path){
+  size_t where = path.rfind("/");
+  if (where != string::npos) return path.substr(1+where);
+  return path;
+}
+
 void whatsit::bind(){
   stringstream foo;
-  foo << progname << suffix 
+  foo << basename(progname) << suffix 
     << "[" << mypid << "]";
   progid = foo.str();
 }
@@ -166,6 +176,7 @@ void scan(const string progid, const string p, const int copies=1){
           if (mod_age != ac_age) cout << "!";
         } else if (mod_age < minimum_age) {
           cout << " young";
+          if (mod_age != ac_age) cout << "!";
         } else if (mod_age == ac_age) {
           cout << " unused";
         } else if (mod_age > maximum_age) {
@@ -185,12 +196,15 @@ void scan(const string progid, const string p, const int copies=1){
 
 void whatsit::update(const string msg, const timeval new_mod,
      const timeval new_ac, const int penalty){
-  cerr << progid << ": "
-       << msg << ": " << ipbase;
-  if (hostname.length()) cerr <<  " " << hostname;
-  cerr << "  mod_age: " << time_out(mod_age)
-          << "  ac_age: " << time_out(ac_age)
-          << endl;
+  if (verbosity){
+    cerr << progid << ": ";
+    if (penalty) cerr << " penalty+";
+    cerr << msg << ": " << ipbase;
+    if (hostname.length()) cerr <<  " " << hostname;
+    cerr << "  mod_age: " << time_out(mod_age)
+            << "  ac_age: " << time_out(ac_age)
+            << endl;
+  }
   timeval pen_mod(new_mod);
   if (penalty) {
     pen_mod = now;
@@ -213,12 +227,17 @@ int main(int _argc, char** _argv){
   int scanmode(0);
   int copies(1);
   int penalty(0);
+  int check(0);
   while (argc > 0) {
     string arg = argv[0]; argc--; argv++;
     if (prefix(arg, "-scan")) {
       scanmode++;
     } else if (prefix(arg, "-copy"))  {
       copies++;
+    } else if (prefix(arg, "-verbose"))  {
+      foo.verbosity++;
+    } else if (prefix(arg, "-check"))  {
+      check++;
     } else if (prefix(arg, "-penalize")
         || prefix(arg, "-penalty"))  {
       if (!argc){
@@ -244,7 +263,22 @@ int main(int _argc, char** _argv){
     return 0;
   }
 
-  return foo.doit(penalty);
+  int sts = foo.doit(penalty);
+
+// perform some extra checks.
+// Probably a better design would be to
+//  a) make more thorough DNS checks, and
+//  b) move all the DNS checking to a separate module
+  if (check){
+    char* hostvar = getenv("TCPREMOTEHOST");
+    if (!hostvar) {
+      cerr << foo.progid
+        << " from " << foo.ipbase
+        << " ... TCPREMOTEHOST not set???" << endl;
+      exeunt(ex_spam);
+    }
+  }
+  exeunt(sts);
 }
 
 int whatsit::doit(const int penalty){
@@ -254,18 +288,11 @@ int whatsit::doit(const int penalty){
       << " TCPREMOTEIP not set???" << endl;
     // should never happen
     // although you can make it happen using a weird test-harness
-    exeunt(ex_syserr);
+    return(ex_syserr);
   }
   ipbase = ipvar;
   char* hostvar = getenv("TCPREMOTEHOST");
-  if (!hostvar) {
-    cerr << progid
-      << " from " << ipbase
-      << " ... TCPREMOTEHOST not set???" << endl;
-    exeunt(ex_spam);
-  } else {
-    hostname = hostvar;
-  }
+  if (hostvar) hostname = hostvar;
 
 // see if our directory exists:
   struct stat dirstat;
@@ -283,7 +310,7 @@ int whatsit::doit(const int penalty){
         << ": mkdir failed for '"
         << dirname << "' : ";
       perror(0);
-      exeunt(ex_syserr);
+      return(ex_syserr);
     }
   }
 
@@ -304,33 +331,34 @@ int whatsit::doit(const int penalty){
       perror(0);
     }
     close(fd);
-    update("new customer", now, now);
-    exeunt(ex_greylisting);
+    update("new customer", now, now, penalty);
+    return(ex_greylisting);
   }
-// here if stat succeeded
+
+// now for really checking the greylist status:
   mod_age = now.tv_sec - ipstat.st_mtime;
   ac_age = now.tv_sec - ipstat.st_atime;
   timeval mod_orig = {ipstat.st_mtime, 0};
   if (mod_age < 0) {
     update("penalty box", mod_orig, now, penalty);
-    exeunt(ex_spam);
+    return(ex_penaltybox);
   }
   if (mod_age < ac_age){
     update("paroled spammer", now, now, penalty);
-    exeunt(ex_greylisting);
+    return(ex_greylisting);
   }
   if (mod_age < minimum_age) {
     update("early bird", mod_orig, now, penalty);
-    exeunt(ex_greylisting);
+    return(ex_greylisting);
   }
   if (mod_age - ac_age < minimum_age    // early bird, or completely unused
     && mod_age > probation) {           // did not diligently resubmit
     update("disprobation", now, now, penalty);
-    exeunt(ex_greylisting);
+    return(ex_greylisting);
   }
   if (ac_age > maximum_age) {
     update("too old, starting over", now, now, penalty);
-    exeunt(ex_greylisting);
+    return(ex_greylisting);
   }
 // if all checks are passed, must be OK:
   update("returning customer", mod_orig, now, penalty);
diff --git a/tools/hi-q.c b/tools/hi-q.c
index 26e67a7..6aaf302 100644
--- a/tools/hi-q.c
+++ b/tools/hi-q.c
@@ -30,6 +30,7 @@ using namespace std;
 #define bar \
 foo(good, 0) ;\
 foo(spam, 21) ;\
+foo(penaltybox, 22) ;\
 foo(permerr, 31) ;\
 foo(greylisting, 70) ;\
 foo(syserr, 71) ;\
@@ -188,6 +189,7 @@ void exeunt(const int sts) {
   // FIXME: stop other children
   //xxxx cerr << "hi-q: exeunt called with " << sts << endl;
   if (sts == ex_spam) fork_and_wait(post);
+  if (sts == ex_penaltybox) exit(ex_spam);
   exit(sts);
 }
 
@@ -274,15 +276,18 @@ int xclose(int arg){
 
 extern char** environ;
 
+string basename(const string path){
+  size_t where = path.rfind("/");
+  if (where != string::npos) return path.substr(1+where);
+  return path;
+}
+
 int main(int argc, char** argv) {
   {
     progname = *argv;
     mypid = getpid();
-    string shortname = progname;
-    size_t where = shortname.rfind("/");
-    if (where != string::npos) shortname = shortname.substr(1+where);
     stringstream binder;
-    binder << shortname << "[" << mypid << "]";
+    binder << basename(progname) << "[" << mypid << "]";
     progid = binder.str();
   }
 
@@ -658,7 +663,7 @@ bar
       }
       cerr << progid
          << " concludes: kid[" << kidno << "]"
-         << " i.e. " << filter[kidno].cmd[0] 
+         << " i.e. " << basename(filter[kidno].cmd[0])
          << "[" <<  argbest_blame << "]"
          << " reports " << exword << endl;
       exeunt(excode);
@@ -666,7 +671,7 @@ bar
       int sig = WTERMSIG(best_blame);
       cerr << progid
          << " concludes: kid[" << kidno << "]"
-         << " i.e. " << filter[kidno].cmd[0] 
+         << " i.e. " << basename(filter[kidno].cmd[0])
          << "[" <<  argbest_blame << "]"
              << " was killed by signal " << sig
              << endl;
@@ -690,7 +695,7 @@ bar
         int sts = WEXITSTATUS(kidstatus);
         cerr << progid
                 << " says: qq program" 
-                << " i.e. " << filter[nkids-1].cmd[0] 
+                << " i.e. " << basename(filter[nkids-1].cmd[0])
                 << "[" <<  kidpid[nkids-1] << "]"
                 << " returned status " << sts
                 << endl;
@@ -698,7 +703,7 @@ bar
       } else if (WIFSIGNALED(kidstatus)) {
         cerr << progid
                 << " says: qq program" 
-                << " i.e. " << filter[nkids-1].cmd[0] 
+                << " i.e. " << basename(filter[nkids-1].cmd[0])
                 << "[" <<  kidpid[nkids-1] << "]"
                 << " was killed by signal " << WTERMSIG(kidstatus)
                 << endl;
diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf
index 90997b4..370ba77 100644
--- a/tools/hi-test2.conf
+++ b/tools/hi-test2.conf
@@ -1,6 +1,6 @@
 stub    hi-test x0 -snooze 2
 stub    hi-test x0 -snooze 1 -exit 0
-stub    greylist -suffix (sfx)
+stub    greylist -suffix (sfx) -v -p 10
 qq      hi-test x1 -snooze 3 -exit 3
 
 postspam /bin/echo post spam !
-- 
cgit v1.2.3