summaryrefslogtreecommitdiff
path: root/tools/greylist.c
blob: d769ff44bb2572b6af4b3c3bdaa192fa7356f648 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <stdlib.h>             /* for exit(), getenv() */
#include <iostream>
#include <string>

#include <sys/types.h>          /* for stat() */
#include <sys/stat.h>           /* for stat() */
#include <unistd.h>             /* for stat() */
#include <stdio.h>              /* for perror */
#include <errno.h>              /* for ENOENT */
#include <fstream>              /* for ofstream() */
#include <fcntl.h>              /* for creat() */
#include <sys/time.h>           /* for gettimeofday() */

using namespace std;

const int sa_good = 0;
const int bug_bait_grey = 1;
// qmail_queue and spamc have similar interpretations here:
const int sa_syserr = 71;

pid_t mypid;
string progname;

void dump(const string var){
  char* str = getenv(var.c_str());
  cerr << progname
    << "[" << mypid << "] "
    << var;
  if (str) cerr << " is set to '" << str << "'" << endl;
  else  cerr << " is not set." << endl;
}

const string dirname("/var/qmail/greylist");

   //       int stat(const char *path, struct stat *buf);
   //       int fstat(int fd, struct stat *buf);
   //       int lstat(const char *path, struct stat *buf);

const int minute(60);
const int hour(60*minute);
const int day(24*hour);

class whatsit{
public:
  string progname;
  pid_t mypid;
  timeval now;
  string ipname;
  int mod_age;
  int ac_age;

  whatsit(const string name)
  : progname(name), mypid(getpid())
  {
    gettimeofday(&now, NULL);
  }
  int doit();
// access comes after modification:
  void update(const string msg, const timeval new_mod, const timeval new_ac);
};

void whatsit::update(const string msg, const timeval new_mod, const timeval new_ac){
  cerr << progname << ": "
       << msg << ": " << ipname
          << "  mod_age: " << mod_age
          << "  ac_age: " << ac_age
          << endl;
  timeval upd[2] = {
// beware:  access illogically comes *before* modification here:
    new_ac,
    new_mod
  };
  utimes(ipname.c_str(), upd);
}

int main(int argc, char** argv){

//  dump("TCPREMOTEIP");
//  dump("TCPREMOTEHOST");

  whatsit foo(argv[0]);
  return foo.doit();
}

int whatsit::doit(){
  char* ipvar = getenv("TCPREMOTEIP");
  if (!ipvar) {
    cerr << progname << ": TCPREMOTEIP not set???" << endl;
    exit(sa_syserr);
  }
  string ipbase = ipvar;

// see if our directory exists:
  struct stat dirstat;
  int rslt = stat(dirname.c_str(), &dirstat);
  if (rslt != 0){
    if (errno != ENOENT) {
      cerr << progname << ": stat failed for '"
        << dirname << "' : ";
      perror(0);
    }
    rslt = mkdir(dirname.c_str(), 0755);
    if (rslt != 0) {
      cerr << progname
        << "uid " << getuid()
        << ": mkdir failed for '"
        << dirname << "' : ";
      perror(0);
      exit(sa_syserr);
    }
  }

  ipname = dirname + "/" + ipbase;
  struct stat ipstat;
  rslt = stat(ipname.c_str(), &ipstat);
  if (rslt != 0){
    if (errno != ENOENT) {
      cerr << progname << ": stat failed for '"
        << ipname << "' : ";
      perror(0);
    }
    ofstream foo;
    int fd = creat(ipname.c_str(), 0644);
    if (fd < 0){
      cerr << progname << ": create failed for '"
        << ipname << "' : ";
      perror(0);
    }
    close(fd);
    update("new customer", now, now);
    return(bug_bait_grey);
  }
// here if stat succeeded
  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 < 5*minute) {
    update("early bird", mod_orig, now);
    return(bug_bait_grey);
  }
  if (ac_age < 32*day) {
    update("returning customer", mod_orig, now);
    return 0;
  }

// here if it is too old:
  update("too old, starting over", now, now);
  return(bug_bait_grey);
}