From cbbd45a9700a660bcc4e2f762f5004c2aa2b1078 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 14:33:26 -0700 Subject: implement "-addr" option in mail-scan --- tools/makefile | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/makefile') diff --git a/tools/makefile b/tools/makefile index 7f5dcef..e092702 100644 --- a/tools/makefile +++ b/tools/makefile @@ -14,6 +14,9 @@ progs = pido hi-q skrewt hi-test mail-scan all: $(progs) +mail-scan: mail-scan.o + $(CC) $< -lboost_regex -o $@ + install: install $(progs) /var/qmail/bin/ cp filters.conf aufilters.conf /var/qmail/control/ -- cgit v1.2.3 From 8fe5ccdb4ee79b4d287b82e80004e1acc9ee4b94 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 14:10:35 -0700 Subject: teach hi-q to have a "mode" word at the front of each line --- tools/aufilters.conf | 2 +- tools/filters.conf | 6 ++-- tools/hi-q.c | 82 ++++++++++++++++++++++++++++++++-------------------- tools/hi-test.conf | 9 ++++-- tools/hi-test2.conf | 6 ++-- tools/makefile | 2 -- 6 files changed, 63 insertions(+), 44 deletions(-) (limited to 'tools/makefile') diff --git a/tools/aufilters.conf b/tools/aufilters.conf index 2eea3fd..e166133 100644 --- a/tools/aufilters.conf +++ b/tools/aufilters.conf @@ -1,2 +1,2 @@ # configuration file for hi-q (authorized users) - /var/qmail/bin/qmail-queue +qq /var/qmail/bin/qmail-queue diff --git a/tools/filters.conf b/tools/filters.conf index 169f74d..8bc2efe 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -1,4 +1,4 @@ # configuration file for hi-q - /var/qmail/bin/skrewt - /usr/local/bin/spamc -Y 0 -s 1000000 - /var/qmail/bin/qmail-queue +black /var/qmail/bin/skrewt +black /usr/local/bin/spamc -Y 0 -s 1000000 +qq /var/qmail/bin/qmail-queue diff --git a/tools/hi-q.c b/tools/hi-q.c index 81e717a..6140206 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -134,6 +134,39 @@ int xclose(int arg){ extern char** environ; +typedef enum {gray, black, qq, fail} moder; + +class jobber{ +public: + moder mode; + vector cmd; + + jobber(const moder _mode, const vector _cmd) + : mode(_mode), cmd(_cmd) + {} + + jobber(const string _mode, const vector _cmd) + : mode(fail), cmd(_cmd){ + setmode(_mode); + } + + jobber() + : mode(fail), cmd(0) + {} + + void setmode(const string _mode) { + if (0) {} + else if (_mode == "gray") mode = gray; + else if (_mode == "grey") mode = gray; // variant spelling + else if (_mode == "black") mode = black; + else if (_mode == "qq") mode = qq; + else { + cerr << "jobber: bad mode: " << _mode << endl; + mode = fail; + } + } +}; + int main(int argc, char** argv) { int verbose(0); int kidstatus; @@ -141,27 +174,8 @@ int main(int argc, char** argv) { int rslt; int loose_end = 0; -#ifdef SpareStuff - char* slurp2_args[] = {"/home/jsd/hack/slurp2", 0}; - char* echo_args[] = {"/bin/echo", "hi there", 0}; - char* wc_args[] = {"/usr/bin/wc", 0}; - char* cat_args[] = {"/bin/cat", 0}; - char* spama_args[] = {"/usr/local/bin/spamassassin", "-e", 0}; - char* spamc_args[] = {"/usr/local/bin/spamc", "-Z", "7", 0}; - char* qq_args[] = {"/var/qmail/bin/qmail-queue", 0}; - - - - const char** joblist[] = { - cat_args, - slurp2_args, - 0 // required: zero terminates the list - }; - -#endif - typedef vector VS; - vector filter; + vector filter; string conf_var = "HI_Q_CONF"; char* auth = getenv("QMAIL_AUTHORIZED"); if (auth && *auth) conf_var = "HI_Q_AUCONF"; @@ -199,15 +213,19 @@ int main(int argc, char** argv) { string line; if (!getline(conf, line).good()) break; istringstream parse(line); - vector job; + jobber job; while (parse.good()){ string token; parse >> token; if (parse.fail()) break; if (token[0] == '#') break; - job.push_back(token); + job.cmd.push_back(token); + } + if (job.cmd.size()) { + job.setmode(job.cmd.front()); + job.cmd.erase(job.cmd.begin()); } - if (job.size()) filter.push_back(job); + if (job.cmd.size()) filter.push_back(job); } unsigned int nkids = filter.size(); @@ -218,8 +236,8 @@ int main(int argc, char** argv) { if (0 && verbose) for (unsigned int ii = 0; ii < nkids; ii++) { cerr << "hi-q filter[" << ii << "] :; "; - for (VS::const_iterator token = filter[ii].begin(); - token != filter[ii].end(); token++){ + for (VS::const_iterator token = filter[ii].cmd.begin(); + token != filter[ii].cmd.end(); token++){ cerr << *token << " "; } cerr << endl; @@ -351,10 +369,10 @@ int main(int argc, char** argv) { // (except last kid reads fd1 as well as fd0). //// probe_fd(); - int ntok = filter[ii].size(); + int ntok = filter[ii].cmd.size(); const char* prog[1+ntok]; for (int jj = 0; jj < ntok; jj++){ - prog[jj] = filter[ii][jj].c_str(); + prog[jj] = filter[ii].cmd[jj].c_str(); } prog[ntok] = 0; close(resync[rEnd]); @@ -417,8 +435,8 @@ int main(int argc, char** argv) { cerr << "hi-q filter[" << ii << "] " << kidpid[ii] << " :; "; - for (VS::const_iterator token = filter[ii].begin(); - token != filter[ii].end(); token++){ + for (VS::const_iterator token = filter[ii].cmd.begin(); + token != filter[ii].cmd.end(); token++){ cerr << *token << " "; } cerr << endl; @@ -477,7 +495,7 @@ int main(int argc, char** argv) { if (sts == 1) { cerr << "hi-q says: kid[" << kidno << "]" << " pid " << argbest_blame - << " i.e. '" << filter[kidno][0] << "'" + << " i.e. '" << filter[kidno].cmd[0] << "'" << " reports spam." << endl; panic(ex_spam); } @@ -515,13 +533,13 @@ int main(int argc, char** argv) { if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); cerr << "hi-q says: qq program " << kidpid[nkids-1] - << " i.e. '" << filter[nkids-1][0] << "'" + << " i.e. '" << filter[nkids-1].cmd[0] << "'" << " returned status " << sts << endl; return sts; } else if (WIFSIGNALED(kidstatus)) { cerr << "hi-q says: qq program " << kidpid[nkids-1] - << " i.e. '" << filter[nkids-1][0] << "'" + << " i.e. '" << filter[nkids-1].cmd[0] << "'" << " was killed by signal " << WTERMSIG(kidstatus) << endl; return ex_syserr; diff --git a/tools/hi-test.conf b/tools/hi-test.conf index 630e2bb..d400373 100644 --- a/tools/hi-test.conf +++ b/tools/hi-test.conf @@ -1,3 +1,6 @@ -hi-test x0 -snooze 10 -hi-test x1 -snooze 1 -exit 1 -kill -hi-test x2 -snooze 10 +# comment + +# another comment, with blank line between +black hi-test x0 -snooze 10 +black hi-test x1 -snooze 1 -exit 1 -kill +qq hi-test x2 -snooze 10 diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index 2dbad3b..df047ab 100644 --- a/tools/hi-test2.conf +++ b/tools/hi-test2.conf @@ -1,3 +1,3 @@ -hi-test x0 -snooze 10 -hi-test x2 -snooze 10 -hi-test x1 -snooze 1 -exit 3 +grey hi-test x0 -snooze 10 +gray hi-test x2 -snooze 10 +qq hi-test x1 -snooze 1 -exit 3 diff --git a/tools/makefile b/tools/makefile index e092702..bb91f37 100644 --- a/tools/makefile +++ b/tools/makefile @@ -28,8 +28,6 @@ install: chmod u+s /var/qmail/rbin/checkpassword cp smtp.conf /etc/stunnel/ cp pop3.conf /etc/stunnel/ - chmod 640 /var/qmail/control/*.crtkey - chown qmaild /var/qmail/control/*.crtkey install qmail-tls-check_certs /var/qmail/bin/ install spamassassin /etc/init.d/ install qmail /etc/init.d/ -- cgit v1.2.3 From a356f2e89ba2bc25207f2d9605a1d6bcca15d6d7 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 14:56:22 -0700 Subject: log some interesting variables --- tools/hi-q.c | 19 ++++++++++++++----- tools/hi-test.c | 11 ++++++++++- tools/makefile | 5 ++++- 3 files changed, 28 insertions(+), 7 deletions(-) (limited to 'tools/makefile') diff --git a/tools/hi-q.c b/tools/hi-q.c index 6140206..2ddc448 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -120,11 +120,16 @@ void usage() { // we have data coming in on fd 0. // and envelope / control information coming in on fd 1. +string progname; +pid_t mypid; + void dump(const string var){ char* str = getenv(var.c_str()); - if (str) cerr << "hi-q: " << var - << " is set to '" << str << "'" << endl; - else cerr << "hi-q: " << var << " is not set." << endl; + cerr << progname + << "[" << mypid << "] " + << var; + if (str) cerr << " is set to '" << str << "'" << endl; + else cerr << " is not set." << endl; } int xclose(int arg){ @@ -149,7 +154,7 @@ public: : mode(fail), cmd(_cmd){ setmode(_mode); } - + jobber() : mode(fail), cmd(0) {} @@ -168,6 +173,10 @@ public: }; int main(int argc, char** argv) { + progname = *argv; + mypid = getpid(); + dump("TCPREMOTEIP"); + dump("TCPREMOTEHOST"); int verbose(0); int kidstatus; @@ -534,7 +543,7 @@ int main(int argc, char** argv) { int sts = WEXITSTATUS(kidstatus); cerr << "hi-q says: qq program " << kidpid[nkids-1] << " i.e. '" << filter[nkids-1].cmd[0] << "'" - << " returned status " << sts + << " returned status " << sts << endl; return sts; } else if (WIFSIGNALED(kidstatus)) { diff --git a/tools/hi-test.c b/tools/hi-test.c index 0c9a35f..47128a9 100644 --- a/tools/hi-test.c +++ b/tools/hi-test.c @@ -41,13 +41,22 @@ void exeunt(const int sts){ using namespace std; +string progname; + +void dump(const string var){ + char* str = getenv(var.c_str()); + cerr << progname << ": " << var; + if (str) cerr << " is set to '" << str << "'" << endl; + else cerr << " is not set." << endl; +} + int main(int _argc, const char** _argv){ int snooze(0); int status(0); int killmode(0); int argc(_argc); const char **argv(_argv); - string progname(*argv); argv++; argc--; + progname = *argv; argv++; argc--; while (argc) { string arg(*argv); argv++; argc--; diff --git a/tools/makefile b/tools/makefile index bb91f37..04b9d21 100644 --- a/tools/makefile +++ b/tools/makefile @@ -6,7 +6,7 @@ CC= /usr/bin/g++ -Wall -g -I $(HOME)/lib/include .PHONY : shipit clean list-src ALWAYS foo dirs setup imgs \ - zip wc html all hacha hevea tcprules + zip wc html all hacha hevea tcprules logmark .SECONDARY : # do not remove any intermediate files @@ -37,6 +37,9 @@ install: /etc/tcpserver/smtp.rules : ./mk_smtp_rules $@ +logmark: + logger -t jsd -p mail.info ========================= + ALWAYS: @echo ... -- cgit v1.2.3 From 379794ea0d610165e75fca2c71e7161d66e0c10d Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 15:20:43 -0700 Subject: add greylist --- tools/greylist.c | 5 +++++ tools/hi-test2.conf | 2 +- tools/makefile | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tools/greylist.c (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c new file mode 100644 index 0000000..fa7d701 --- /dev/null +++ b/tools/greylist.c @@ -0,0 +1,5 @@ + + +int main(){ + return 0; +} diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index df047ab..c7312fb 100644 --- a/tools/hi-test2.conf +++ b/tools/hi-test2.conf @@ -1,3 +1,3 @@ grey hi-test x0 -snooze 10 -gray hi-test x2 -snooze 10 +gray greylist x2 -snooze 10 qq hi-test x1 -snooze 1 -exit 3 diff --git a/tools/makefile b/tools/makefile index 04b9d21..8837952 100644 --- a/tools/makefile +++ b/tools/makefile @@ -10,7 +10,7 @@ CC= /usr/bin/g++ -Wall -g -I $(HOME)/lib/include .SECONDARY : # do not remove any intermediate files -progs = pido hi-q skrewt hi-test mail-scan +progs = pido hi-q skrewt hi-test mail-scan greylist all: $(progs) -- cgit v1.2.3 From 945767f12154698fab3e7e370486a5e9b09276e9 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 20:58:24 -0700 Subject: add "Scan" function to greylist --- tools/greylist.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++---------- tools/makefile | 3 ++ 2 files changed, 77 insertions(+), 15 deletions(-) (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c index d769ff4..3ba502d 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -1,5 +1,6 @@ #include /* for exit(), getenv() */ #include +#include #include #include /* for stat() */ @@ -11,8 +12,16 @@ #include /* for creat() */ #include /* for gettimeofday() */ +// requires apt-get install libboost-filesystem-dev: +#include + using namespace std; +const int minute(60); +const int hour(60*minute); +const int day(24*hour); + +const int minimum_age(5*minute); const int sa_good = 0; const int bug_bait_grey = 1; // qmail_queue and spamc have similar interpretations here: @@ -30,27 +39,26 @@ void dump(const string var){ 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 dirname; string progname; pid_t mypid; timeval now; + string ipbase; string ipname; + string hostname; int mod_age; int ac_age; - whatsit(const string name) - : progname(name), mypid(getpid()) + whatsit(const string name, const string _dirname) + : dirname(_dirname), progname(name), mypid(getpid()), mod_age(0), ac_age(0) { gettimeofday(&now, NULL); } @@ -59,10 +67,49 @@ public: void update(const string msg, const timeval new_mod, const timeval new_ac); }; +void scan(const string p){ + timeval now; + gettimeofday(&now, NULL); + using namespace boost::filesystem; + + if (is_directory(p)) { + for (directory_iterator itr(p); itr!=directory_iterator(); ++itr) { + string basename = itr->path().filename(); + cout << setw(20) << basename << ' '; // display filename only + if (is_regular_file(itr->status())) { +// cout << " [" << file_size(itr->path()) << ']'; + struct stat mystat; + string fn = p + "/" + basename; + int rslt = stat(fn.c_str(), &mystat); + if (rslt != 0){ + cerr << progname << ": stat failed for '" + << fn << "' : "; + perror(0); + } + int mod_age = now.tv_sec - mystat.st_mtime; + int ac_age = now.tv_sec - mystat.st_atime; + cout << setw(10) << mod_age + << " " << setw(10) << ac_age; + if (mod_age < minimum_age) { + cout << " young"; + } else if (mod_age == ac_age) { + cout << " never used"; + } + } + cout << '\n'; + } + } + else { + // starting point is not a directory: + cout << (exists(p) ? "Found: " : "Not found: ") << p << '\n'; + } +} + void whatsit::update(const string msg, const timeval new_mod, const timeval new_ac){ cerr << progname << ": " - << msg << ": " << ipname - << " mod_age: " << mod_age + << msg << ": " << ipbase; + if (hostname.length()) cerr << " " << hostname; + cerr << " mod_age: " << mod_age << " ac_age: " << ac_age << endl; timeval upd[2] = { @@ -73,12 +120,22 @@ void whatsit::update(const string msg, const timeval new_mod, const timeval new_ utimes(ipname.c_str(), upd); } -int main(int argc, char** argv){ - -// dump("TCPREMOTEIP"); -// dump("TCPREMOTEHOST"); +int main(int _argc, char** _argv){ + int argc(_argc); + char** argv(_argv); + const string dirname("/var/qmail/greylist"); + whatsit foo(argv[0], dirname); argc--; argv++; + while (argc > 0) { + string arg = argv[0]; argc--; argv++; + if (arg == "-scan") { + scan(dirname); + return 0; + } + else { + cerr << "Unrecognized arg, ignored: " << arg << endl; + } + } - whatsit foo(argv[0]); return foo.doit(); } @@ -88,7 +145,9 @@ int whatsit::doit(){ cerr << progname << ": TCPREMOTEIP not set???" << endl; exit(sa_syserr); } - string ipbase = ipvar; + ipbase = ipvar; + char* hostvar = getenv("TCPREMOTEHOST"); + if (hostvar) hostname = hostvar; // see if our directory exists: struct stat dirstat; @@ -134,7 +193,7 @@ int whatsit::doit(){ 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) { + if (mod_age < minimum_age) { update("early bird", mod_orig, now); return(bug_bait_grey); } diff --git a/tools/makefile b/tools/makefile index 8837952..97c345c 100644 --- a/tools/makefile +++ b/tools/makefile @@ -14,6 +14,9 @@ progs = pido hi-q skrewt hi-test mail-scan greylist all: $(progs) +greylist: greylist.c + $(CC) $< -lboost_filesystem-mt -o $@ + mail-scan: mail-scan.o $(CC) $< -lboost_regex -o $@ -- cgit v1.2.3 From 9280a33c63250b841e8a51d4ef3aac2148b4bc12 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 05:43:54 -0700 Subject: 5 minutes is not enough --- qmail.c | 2 +- tools/greylist.c | 2 +- tools/makefile | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'tools/makefile') diff --git a/qmail.c b/qmail.c index 476c788..a8ddef8 100644 --- a/qmail.c +++ b/qmail.c @@ -122,7 +122,7 @@ struct qmail *qq; case 65: /* fall through */ case 66: /* fall through */ case 62: return "Zqq trouble creating files in queue (#4.3.0)"; - case 70: return "Zgreylisting in progress; please try again in 00:05:00 (#4.3.0)"; + case 70: return "Zgreylisting in progress; please try again in 00:15:00 (#4.3.0)"; case 71: return "Zmail server temporarily rejected message (#4.3.0)"; case 72: return "Zconnection to mail server timed out (#4.4.1)"; case 73: return "Zconnection to mail server rejected (#4.4.1)"; diff --git a/tools/greylist.c b/tools/greylist.c index 1e62e93..525fc9b 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -22,7 +22,7 @@ const int minute(60); const int hour(60*minute); const int day(24*hour); -const int minimum_age(5*minute); +const int minimum_age(15*minute); const int maximum_age(32*day); const int probation(4*hour); const int sa_good = 0; diff --git a/tools/makefile b/tools/makefile index 97c345c..3e599b8 100644 --- a/tools/makefile +++ b/tools/makefile @@ -43,6 +43,11 @@ install: logmark: logger -t jsd -p mail.info ========================= +todo: + echo zap -- kill program group \ + extended error codes from skrewt, greylist \ + extended error codes [-x] from spamc + ALWAYS: @echo ... -- cgit v1.2.3 From abcd53ddce872e3f331d8a6d7c1ff44c070b91b0 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 09:20:36 -0700 Subject: greylist will now zap its program-group --- tools/greylist.c | 46 +++++++++++++++++++++++++++++++++++----------- tools/hi-test2.conf | 2 +- tools/makefile | 2 +- 3 files changed, 37 insertions(+), 13 deletions(-) (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c index 525fc9b..910b40c 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -11,7 +11,8 @@ #include /* for ofstream() */ #include /* for creat() */ #include /* for gettimeofday() */ -#include +#include /* for stringstream */ +#include /* for kill(), SIGUSR1 */ // requires apt-get install libboost-filesystem-dev: #include @@ -43,10 +44,32 @@ void dump(const string var){ } - // int stat(const char *path, struct stat *buf); - // int fstat(int fd, struct stat *buf); - // int lstat(const char *path, struct stat *buf); +//////////////// +// little utility to help with argument parsing: +// +int prefix(const string shorter, const string longer){ + return shorter == longer.substr(0, shorter.length()); +} + +void exeunt(const int sts){ + if (sts == sa_good) exit(sts); + + const char* foo = getenv("HI_Q_GROUP"); + if (!foo) exit(sts); + +// No point in signalling ourself: + sighandler_t rslt = signal(SIGUSR1, SIG_IGN); + if (rslt == SIG_ERR) { + cerr << "error setting signal" << endl; + } + int k = kill(-atoi(foo), SIGUSR1); + if (k) { + cerr << "kill failed on group " << atoi(foo) << " ... "; + perror(0); + } + exit(sts); +} class whatsit{ public: @@ -161,7 +184,8 @@ int main(int _argc, char** _argv){ return 0; } else { - cerr << "Unrecognized arg, ignored: " << arg << endl; + cerr << "Unrecognized arg: " << arg << endl; + exeunt(sa_syserr); } } @@ -172,7 +196,7 @@ int whatsit::doit(){ char* ipvar = getenv("TCPREMOTEIP"); if (!ipvar) { cerr << progname << ": TCPREMOTEIP not set???" << endl; - exit(sa_syserr); + exeunt(sa_syserr); } ipbase = ipvar; char* hostvar = getenv("TCPREMOTEHOST"); @@ -194,7 +218,7 @@ int whatsit::doit(){ << ": mkdir failed for '" << dirname << "' : "; perror(0); - exit(sa_syserr); + exeunt(sa_syserr); } } @@ -216,7 +240,7 @@ int whatsit::doit(){ } close(fd); update("new customer", now, now); - return(bug_bait_grey); + exeunt(bug_bait_grey); } // here if stat succeeded mod_age = now.tv_sec - ipstat.st_mtime; @@ -224,16 +248,16 @@ int whatsit::doit(){ timeval mod_orig = {ipstat.st_mtime, 0}; if (mod_age < minimum_age) { update("early bird", mod_orig, now); - return(bug_bait_grey); + exeunt(bug_bait_grey); } if (mod_age - ac_age < minimum_age // early bird, or completely unused && mod_age > probation) { // did not diligently resubmit update("disprobation", now, now); - return(bug_bait_grey); + exeunt(bug_bait_grey); } if (ac_age > maximum_age) { update("too old, starting over", now, now); - return(bug_bait_grey); + exeunt(bug_bait_grey); } // if all checks are passed, must be OK: update("returning customer", mod_orig, now); diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index c7312fb..3c1422c 100644 --- a/tools/hi-test2.conf +++ b/tools/hi-test2.conf @@ -1,3 +1,3 @@ grey hi-test x0 -snooze 10 -gray greylist x2 -snooze 10 +gray greylist qq hi-test x1 -snooze 1 -exit 3 diff --git a/tools/makefile b/tools/makefile index 3e599b8..cf62473 100644 --- a/tools/makefile +++ b/tools/makefile @@ -44,7 +44,7 @@ logmark: logger -t jsd -p mail.info ========================= todo: - echo zap -- kill program group \ + echo zap penalize greylist status of spam \ extended error codes from skrewt, greylist \ extended error codes [-x] from spamc -- cgit v1.2.3 From bcce618000e74f0d36780b48c3c49f4a9b5914e5 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 11:16:12 -0700 Subject: check DNS consistency --- tools/greylist.c | 51 ++++++++++++++++++++++++++++++++++++--------------- tools/makefile | 8 +++++--- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c index f9a4ebc..863a2fe 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -20,6 +20,8 @@ #include /* for getaddrinfo() */ #include /* for getaddrinfo() */ #include /* for memset() */ +#include /* for inet_ntop() */ + using namespace std; @@ -294,7 +296,7 @@ int main(int _argc, char** _argv){ // (b) move all the DNS checking to a separate module int dns = foo.check_dns(); - if (dns == ex_syserr) return dns; + if (dns == ex_syserr || dns == ex_spam) return dns; exeunt(sts); } @@ -373,15 +375,26 @@ int whatsit::doit(const int penalty){ typedef vector VU; -class VUx : public VU { +class VUx{ public: - sa_family_t fam; + VU addr; + sa_family_t fam; + string str(); }; +string VUx::str(){ + char msgbuf[INET6_ADDRSTRLEN]; + const char* rslt = inet_ntop(fam, &addr[0], + msgbuf, sizeof(msgbuf)); + if (!rslt) rslt = ""; + return rslt; +} + VUx parse_sockaddr(const sockaddr* ai_addr) { void* numericAddress; VUx rslt; int addrsize; + rslt.addr = VU(0); rslt.fam = ((sockaddr *)ai_addr)->sa_family; switch (rslt.fam) { case AF_INET: @@ -397,8 +410,7 @@ VUx parse_sockaddr(const sockaddr* ai_addr) { return rslt; } unsigned char* foo = (unsigned char*) numericAddress; - (VU)rslt = VU(foo, foo+addrsize); - cerr << "asdf " << rslt.size() << " ... " << VU(foo, foo+addrsize).size() << endl; + rslt.addr = VU(foo, foo+addrsize); return rslt; } @@ -443,7 +455,7 @@ int whatsit::check_dns(){ return ex_syserr; } - VU ipAddr = parse_sockaddr(ipresult->ai_addr); + VUx ipAddr = parse_sockaddr(ipresult->ai_addr); error = getaddrinfo(hostvar, NULL, &hints, &result); if (error) { cerr << "error in getaddrinfo for " << hostvar @@ -454,18 +466,27 @@ int whatsit::check_dns(){ // loop over all returned results and check for a match. vector checked_hosts; for (res = result; res != NULL; res = res->ai_next){ - VU hostAddr = parse_sockaddr(res->ai_addr); -#if 0 - char msgbuf[INET6_ADDRSTRLEN]; - const char* rslt = inet_ntop(fam, numericAddress, - msgbuf, sizeof(msgbuf)); -#endif - if (!diff(hostAddr, ipAddr)) { - cerr << "match! " << ipAddr.size() << endl; + VUx hostAddr = parse_sockaddr(res->ai_addr); + + if (!diff(hostAddr.addr, ipAddr.addr)) { + ///// cerr << "match! " << ipAddr.addr.size() << endl; goto done; } } - cerr << "no match" << endl; + cerr << "(warning) DNS inconsistency: " + << ipAddr.str() << " does not match"; + for (res = result; res != NULL; res = res->ai_next){ + cerr << " " << parse_sockaddr(res->ai_addr).str(); + } + cerr << endl; +#if 1 + // temporary ... just a warning + return 0; +#else + return ex_spam; +#endif + + done: return 0; } diff --git a/tools/makefile b/tools/makefile index cf62473..1f878f0 100644 --- a/tools/makefile +++ b/tools/makefile @@ -44,9 +44,11 @@ logmark: logger -t jsd -p mail.info ========================= todo: - echo zap penalize greylist status of spam \ - extended error codes from skrewt, greylist \ - extended error codes [-x] from spamc + echo \ + pass message-ID to greylist program \ + ... also provide a way for certain recipients to bypass some checks \ + ... both will require major restructuring, "cat" process \ + ..... ALWAYS: @echo ... -- cgit v1.2.3 From 46cb697732ea2c2c4a68358109a58232ef2666e7 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 11:43:00 -0700 Subject: dns inconsistency is just a warning for the moment --- tools/greylist.c | 22 ++++++++++------------ tools/makefile | 1 + 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c index 863a2fe..95a4f0b 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -436,6 +436,8 @@ int whatsit::check_dns(){ struct addrinfo *res; addrinfo hints; int error; + int ex_dnserr(ex_syserr); + ex_dnserr = 0; // temporarily just a warning /* resolve the domain name into a list of addresses */ memset(&hints, 0, sizeof(struct addrinfo)); @@ -448,11 +450,11 @@ int whatsit::check_dns(){ if (error) { cerr << "error in getaddrinfo for " << ipvar << " : " << gai_strerror(error) << endl; - return ex_syserr; + return ex_dnserr; } if (!ipresult) { cerr << "should never happen (addr with no addrs?)" << endl; - return ex_syserr; + return ex_dnserr; } VUx ipAddr = parse_sockaddr(ipresult->ai_addr); @@ -460,7 +462,7 @@ int whatsit::check_dns(){ if (error) { cerr << "error in getaddrinfo for " << hostvar << " : " << gai_strerror(error) << endl; - return ex_syserr; + return ex_dnserr; } // loop over all returned results and check for a match. @@ -473,19 +475,15 @@ int whatsit::check_dns(){ goto done; } } - cerr << "(warning) DNS inconsistency: " - << ipAddr.str() << " does not match"; + if (!ex_dnserr) cerr << "(warning) "; + cerr << "DNS inconsistency: " + << ipAddr.str() << " --> " + << hostvar << " ==>"; for (res = result; res != NULL; res = res->ai_next){ cerr << " " << parse_sockaddr(res->ai_addr).str(); } cerr << endl; -#if 1 - // temporary ... just a warning - return 0; -#else - return ex_spam; -#endif - + return ex_dnserr; done: return 0; diff --git a/tools/makefile b/tools/makefile index 1f878f0..6fa9636 100644 --- a/tools/makefile +++ b/tools/makefile @@ -48,6 +48,7 @@ todo: pass message-ID to greylist program \ ... also provide a way for certain recipients to bypass some checks \ ... both will require major restructuring, "cat" process \ + ... IPv6 reverse-DNS recors \ ..... ALWAYS: -- cgit v1.2.3 From 8ce08aca2410c795dfc46f37dc27402ff6de5dd1 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 15:53:08 -0700 Subject: ignore penalty features for the moment --- tools/greylist.c | 4 ++++ tools/hi-q.c | 22 ++++++++++++++-------- tools/makefile | 3 ++- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'tools/makefile') diff --git a/tools/greylist.c b/tools/greylist.c index 95a4f0b..063c3d0 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -68,6 +68,10 @@ int prefix(const string shorter, const string longer){ void exeunt(const int sts){ if (sts == ex_good) exit(sts); +#ifndef PENALIZE_SPAMMERS + if (sts == ex_penaltybox) exit(sts); +#endif + const char* foo = getenv("HI_Q_GROUP"); if (!foo) exit(sts); diff --git a/tools/hi-q.c b/tools/hi-q.c index 6aaf302..8766b08 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -158,7 +158,7 @@ int fork_and_wait(const jobber job){ int sts = WEXITSTATUS(kidstatus); if (sts != ex_good && sts != ex_spam) { cerr << "hi-q: job " << prog[0] - << " unexpectedly returns status: " << sts + << " unexpectedly returns status: " << sts << endl; exit(sts); } @@ -179,7 +179,7 @@ int fork_and_wait(const jobber job){ int fork_and_wait(vector post){ for(vector::const_iterator foo = post.begin(); foo != post.end(); foo++) { - int rslt = fork_and_wait(*foo); + int rslt = fork_and_wait(*foo); if (rslt) return rslt; } return 0; @@ -590,7 +590,7 @@ bar // do not decrement the "alive" counter // since that only applies to non-special kids if (WIFEXITED(kidstatus)) { - cerr << "hi-q: special kid exited early, status " + cerr << "hi-q: special kid exited early, status " << WEXITSTATUS(kidstatus) << " with " << alive << " kids still alive" << endl; @@ -599,10 +599,10 @@ bar int sig = WTERMSIG(kidstatus); if (sig == SIGUSR1) {/* normal, no logging required */} else { - cerr << "hi-q: special kid killed by signal " + cerr << "hi-q: special kid killed by signal " << sig << endl; // this is not normal - return(ex_syserr); + return(ex_syserr); } } else { /* paused, not dead */ @@ -612,7 +612,13 @@ bar // here if somekid is not the special kid if (WIFEXITED(kidstatus)) { alive--; - if (WEXITSTATUS(kidstatus)) { + int sts = WEXITSTATUS(kidstatus); +#ifndef PENALIZE_SPAMMERS + // ignore penalties for the moment + // to see whether there are any false positives + if (sts == ex_penaltybox) sts = ex_good; +#endif + if (sts) { argbest_blame = somekid; best_blame = kidstatus; break; @@ -694,7 +700,7 @@ bar if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); cerr << progid - << " says: qq program" + << " says: qq program" << " i.e. " << basename(filter[nkids-1].cmd[0]) << "[" << kidpid[nkids-1] << "]" << " returned status " << sts @@ -702,7 +708,7 @@ bar return sts; } else if (WIFSIGNALED(kidstatus)) { cerr << progid - << " says: qq program" + << " says: qq program" << " i.e. " << basename(filter[nkids-1].cmd[0]) << "[" << kidpid[nkids-1] << "]" << " was killed by signal " << WTERMSIG(kidstatus) diff --git a/tools/makefile b/tools/makefile index 6fa9636..9059a2f 100644 --- a/tools/makefile +++ b/tools/makefile @@ -49,7 +49,8 @@ todo: ... also provide a way for certain recipients to bypass some checks \ ... both will require major restructuring, "cat" process \ ... IPv6 reverse-DNS recors \ - ..... + ... "clean up bad DNS reports nnnn --> () ==> ()" \ + ..... ALWAYS: @echo ... -- cgit v1.2.3 From abb71cf6b1145588827d04de4da3bb48ecb06965 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 22 Jul 2012 14:43:07 -0700 Subject: set program gid (not just egid) the way mailman likes it --- tools/makefile | 22 ++++++++++++++++------ tools/wripper.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 tools/wripper.c (limited to 'tools/makefile') diff --git a/tools/makefile b/tools/makefile index 9059a2f..43418ce 100644 --- a/tools/makefile +++ b/tools/makefile @@ -10,18 +10,24 @@ CC= /usr/bin/g++ -Wall -g -I $(HOME)/lib/include .SECONDARY : # do not remove any intermediate files -progs = pido hi-q skrewt hi-test mail-scan greylist +qprogs = pido hi-q skrewt hi-test mail-scan greylist wripper -all: $(progs) +all: $(qprogs) wripper -greylist: greylist.c +greylist: greylist.o $(CC) $< -lboost_filesystem-mt -o $@ +wripper: wripper.o + $(CC) $< -o $@ + chgrp daemon $@ + chmod g+s $@ + mail-scan: mail-scan.o $(CC) $< -lboost_regex -o $@ install: - install $(progs) /var/qmail/bin/ + install $(qprogs) /var/qmail/bin/ + install -gdaemon -m2755 wripper /usr/lib/mailman/mail/ cp filters.conf aufilters.conf /var/qmail/control/ install -m700 -d /var/qmail/rbin chown qmaild /var/qmail/rbin @@ -43,14 +49,18 @@ install: logmark: logger -t jsd -p mail.info ========================= -todo: +# Command to let everybody out of the penalty box: +parole: + greylist -scan |grep penalty | while read addr rest ; do TCPREMOTEIP=$addr greylist -p 1 -v ; done + +todo: echo \ pass message-ID to greylist program \ ... also provide a way for certain recipients to bypass some checks \ ... both will require major restructuring, "cat" process \ ... IPv6 reverse-DNS recors \ ... "clean up bad DNS reports nnnn --> () ==> ()" \ - ..... + ..... ALWAYS: @echo ... diff --git a/tools/wripper.c b/tools/wripper.c new file mode 100644 index 0000000..2a19c8b --- /dev/null +++ b/tools/wripper.c @@ -0,0 +1,53 @@ +////////////// + +using namespace std; +#include +#include +#include +#include +#include /* for perror() */ + +string dirname(const string path){ + size_t where = path.rfind("/"); + if (where == string::npos) return "."; + return path.substr(0, where); +} + +int main(int argc, char** argv){ + int uid=getuid(); + int euid=geteuid(); + int gid=getgid(); + int egid=getegid(); + int sts; + int verbosity(0); + + if (verbosity) cout << "uid: " << uid + << " euid: " << euid + << " gid: " << gid + << " egid: " << egid + << endl; + + sts = setreuid(euid, euid); + if (sts){ + cerr << "wripper: setreuid failed: "; + perror(0); + } + + sts = setregid(egid, egid); + if (sts){ + cerr << "wripper: setregid failed: "; + perror(0); + } + + if (verbosity) cout << "uid: " << getuid() + << " euid: " << geteuid() + << " gid: " << getgid() + << " egid: " << getegid() + << endl; + + string path = dirname(*argv) + "/mailman"; + *argv = (char*) path.c_str(); + execv(*argv, argv); + cerr << "wripper: exec failed for '" << *argv << "' : "; + perror(0); +} -- cgit v1.2.3 From 8f18b37fd5a46d28544a4f31465c47428a43398b Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 22 Jul 2012 19:13:11 -0700 Subject: create the "bash-c" program, so as to make it easy to write self-executing scripts --- .gitignore | 3 +++ tools/bash-c.c | 36 ++++++++++++++++++++++++++++++++++++ tools/hi-test.conf | 2 +- tools/hi-test2.conf | 10 ++++++---- tools/hi-test3.conf | 2 +- tools/makefile | 5 ++++- tools/t-bash-c | 3 +++ 7 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 tools/bash-c.c mode change 100644 => 100755 tools/hi-test.conf mode change 100644 => 100755 tools/hi-test2.conf create mode 100755 tools/t-bash-c (limited to 'tools/makefile') diff --git a/.gitignore b/.gitignore index ca1ef6d..574561f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.rej *.logg *#[0-9]* +\#*# has?????.h auto-gid @@ -158,3 +159,5 @@ auto_uids.c control.tar.gz data.tar.gz dummy-mail-transfer-agent_all.deb +bash-c +wripper diff --git a/tools/bash-c.c b/tools/bash-c.c new file mode 100644 index 0000000..6b2844d --- /dev/null +++ b/tools/bash-c.c @@ -0,0 +1,36 @@ +////////////// + +using namespace std; +#include +#include +#include +#include +#include /* for perror() */ +#include /* for exit() */ + + +string dirname(const string path){ + size_t where = path.rfind("/"); + if (where == string::npos) return "."; + return path.substr(0, where); +} + +int main(int argc, char** argv){ + int verbosity(0); + + char* nargv[1+argc]; + for (int ii = 1; ii <= argc; ii++){ + if (verbosity) { + if (argv[ii] == 0) cout << "zero" << endl; + else cout << "[" << argv[ii] << "]" << endl; + } + nargv[1+ii] = argv[ii]; + } + nargv[1] = (char*)"-c"; + nargv[0] = (char*)"/home/jsd/bin/ECHO"; + nargv[0] = (char*)"/bin/bash"; + + execv(*nargv, nargv); + cerr << "bash-c: exec failed for '" << *nargv << "' : "; + perror(0); +} diff --git a/tools/hi-test.conf b/tools/hi-test.conf old mode 100644 new mode 100755 index aa6a1cf..f692f37 --- a/tools/hi-test.conf +++ b/tools/hi-test.conf @@ -1,4 +1,4 @@ -# comment +#! /usr/local/bin/bash-c set -x ; Date: Mon, 23 Jul 2012 08:43:49 -0700 Subject: the usual automatic dependency-finder --- tools/makefile | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'tools/makefile') diff --git a/tools/makefile b/tools/makefile index 5dc7952..08bf17c 100644 --- a/tools/makefile +++ b/tools/makefile @@ -10,12 +10,33 @@ CC= /usr/bin/g++ -Wall -g -I $(HOME)/lib/include .SECONDARY : # do not remove any intermediate files -qprogs = pido hi-q skrewt hi-test mail-scan greylist wripper +# sources for main programs that go in /var/qmail/bin +qmain = pido.c hi-q.c skrewt.c hi-test.c mail-scan.c greylist.c wripper.c +qprogs = $(qmain:%.c=%) -moreprogs = wripper bash-c +# sources for other main programs: +moremain = wripper.c bash-c.c +moreprogs = $(moremain:%.c=%) + +nonmain = + +sources = $(qmain) $(moremain) $(nonmain) + +beware_other = checkpassword.c spamc.c + +## dependency-finding scheme (with local mods) based on: +## http://www.gnu.org/manual/make-3.77/html_mono/make.html#SEC42 +## (see also include statement at end of this makefile) +%.d : %.c + @$(SHELL) -ec '$(CXX) -MM $(CXXFLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ + [ -s $@ ] || rm -f $@' all: $(qprogs) $(moreprogs) +show: + : --- $(qprogs) +++ $(moreprogs) + greylist: greylist.o $(CC) $< -lboost_filesystem-mt -o $@ @@ -70,3 +91,5 @@ ALWAYS: ##?? include $(chapters:.htm=.d) ##?? include $(fancy:%.htm=aux/%.fig) + +include $(sources:.c=.d) -- cgit v1.2.3 From 02bfae1f87c4693eb00bf943fb24886a5ac47a09 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 24 Jul 2012 11:29:56 -0700 Subject: improve compatibility across boost versions; allow use by non-superuser --- tools/makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tools/makefile') diff --git a/tools/makefile b/tools/makefile index 08bf17c..76df23b 100644 --- a/tools/makefile +++ b/tools/makefile @@ -38,12 +38,13 @@ show: : --- $(qprogs) +++ $(moreprogs) greylist: greylist.o - $(CC) $< -lboost_filesystem-mt -o $@ + $(CC) $< -lboost_filesystem-mt -lboost_system -o $@ + +# $(CC) $< -lboost_filesystem -o $@ wripper: wripper.o $(CC) $< -o $@ - chgrp daemon $@ - chmod g+s $@ + chgrp daemon $@ && chmod g+s $@ || true mail-scan: mail-scan.o $(CC) $< -lboost_regex -o $@ -- cgit v1.2.3 From d2564d25e802d1ee3230cf045c4940e836b5c6a2 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 16:50:11 -0700 Subject: split ltgrey (and libltgrey) off from greylist; put some utility functions into their own file. --- .gitignore | 1 + tools/greylist.c | 50 +------- tools/libltgrey.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/libltgrey.h | 38 ++++++ tools/ltgrey.c | 153 ++++++++++++++++++++++ tools/makefile | 12 +- tools/qq_exit_codes.h | 15 +++ tools/skrewt.c | 17 +-- tools/utils.c | 44 +++++++ tools/utils.h | 3 + 10 files changed, 611 insertions(+), 65 deletions(-) create mode 100644 tools/libltgrey.c create mode 100644 tools/libltgrey.h create mode 100644 tools/ltgrey.c create mode 100644 tools/qq_exit_codes.h create mode 100644 tools/utils.c create mode 100644 tools/utils.h (limited to 'tools/makefile') diff --git a/.gitignore b/.gitignore index e929027..b6369d1 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,4 @@ data.tar.gz dummy-mail-transfer-agent_all.deb bash-c wripper +ltgrey diff --git a/tools/greylist.c b/tools/greylist.c index 89396e7..9af70eb 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -22,7 +22,6 @@ #include /* for memset() */ #include /* for inet_ntop() */ - using namespace std; const int minute(60); @@ -33,18 +32,7 @@ const int minimum_age(15*minute); const int maximum_age(32*day); const int probation(4*hour); -// error exit codes, mostly as stated in qmail.c -#define foo(name, num) const int ex_ ## name = num -#define bar foo(good, 0) ;\ -foo(spam, 21) ;\ -foo(penaltybox, 22) ;\ -foo(badDNS, 23) ;\ -foo(greylisting, 70) ;\ -foo(syserr, 71) ;\ -foo(comerr, 74) ; - -bar -#undef foo +#include "qq_exit_codes.h" pid_t mypid; string progname; @@ -58,14 +46,6 @@ void dump(const string var){ else cerr << " is not set." << endl; } - -//////////////// -// little utility to help with argument parsing: -// -int prefix(const string shorter, const string longer){ - return shorter == longer.substr(0, shorter.length()); -} - void exeunt(const int sts){ if (sts == ex_good) exit(sts); @@ -93,6 +73,8 @@ void exeunt(const int sts){ exit(sts); } +#include "utils.h" + class whatsit{ public: string dirname; @@ -126,12 +108,6 @@ public: int check_dns_sub(string &addr, string &host, vector &checked); }; -string basename(const string path){ - size_t where = path.rfind("/"); - if (where != string::npos) return path.substr(1+where); - return path; -} - int whatsit::setup(){ stringstream foo; foo << basename(progname) << suffix @@ -145,26 +121,6 @@ int whatsit::setup(){ return 0; } -string time_out(const int _ttt){ - int ttt(abs(_ttt)); - int sec(ttt % 60); - int min((ttt / 60) % 60); - int hr(ttt / 3600); - stringstream foo; - int didsome(0); - if (_ttt < 0) foo << "-"; - if (hr) { - foo << hr << ":"; - didsome++; - } - if (didsome || min){ - foo << setw(didsome?2:1) << setfill('0') << min << ":"; - didsome++; - } - foo << setw(didsome?2:1) << setfill('0') << sec; - return foo.str(); -} - void scan(const string progid, const string p, const int copies=1){ timeval now; gettimeofday(&now, NULL); diff --git a/tools/libltgrey.c b/tools/libltgrey.c new file mode 100644 index 0000000..d4ec0da --- /dev/null +++ b/tools/libltgrey.c @@ -0,0 +1,343 @@ +#include /* for exit(), getenv() */ +#include +#include +#include + +#include /* for stat(), getaddrinfo() */ +#include /* for stat() */ +#include /* for stat() */ +#include /* for perror */ +#include /* for ENOENT */ +#include /* for ofstream() */ +#include /* for creat() */ +#include /* for gettimeofday() */ +#include /* for stringstream */ +#include /* for kill(), SIGUSR1 */ + +// requires apt-get install libboost-filesystem-dev: +#include + +#include /* for getaddrinfo() */ +#include /* for getaddrinfo() */ +#include /* for memset() */ +#include /* for inet_ntop() */ + +using namespace std; + +const int minute(60); +const int hour(60*minute); +const int day(24*hour); + +const int minimum_age(15*minute); +const int maximum_age(32*day); +const int probation(4*hour); + +#if 0 +void exeunt(const int sts){ + if (sts == ex_good) exit(sts); + +#ifndef PENALIZE_SPAMMERS + if (sts == ex_penaltybox) exit(sts); +#endif + +#ifndef KILL_GROUP + exit(sts); +#endif + + const char* foo = getenv("HI_Q_GROUP"); + if (!foo) exit(sts); + +// No point in signalling ourself: + sighandler_t rslt = signal(SIGUSR1, SIG_IGN); + if (rslt == SIG_ERR) { + cerr << "error setting signal" << endl; + } + int k = kill(-atoi(foo), SIGUSR1); + if (k) { + cerr << "kill failed on group " << atoi(foo) << " ... "; + perror(0); + } + exit(sts); +} +#endif + +#include /* for gettimeofday */ +#include /* for setw */ +#include /* for stat */ +#include /* for stat, creat */ +#include /* for stat, creat */ +#include /* for creat */ +#include /* for ofstream() */ + +#include "libltgrey.h" +#include "utils.h" +#include "qq_exit_codes.h" + +void whatsit::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; +} + +int whatsit::setup(){ + stringstream foo; + foo << basename(progname) << suffix + << "[" << mypid << "]"; + progid = foo.str(); + + ipvar = getenv("TCPREMOTEIP"); + if (ipvar) ipbase = ipvar; + hostvar = getenv("TCPREMOTEHOST"); + if (hostvar) hostname = hostvar; + return 0; +} + +void whatsit::update(const string msg, const timeval new_mod, + const timeval new_ac, const int penalty, const int stain){ + if (verbosity){ + if (penalty || stain || verbosity>1) cerr << progid << ": "; + if (penalty) cerr << " penalty " << penalty; + if (stain) cerr << " stain " << stain; + if (verbosity > 1) { + if (penalty || stain) cerr << "+"; // separation, punctuation + cerr << msg << ": " << ipbase; + if (hostname.length()) cerr << " " << hostname; + cerr << " mod_age: " << time_out(mod_age) + << " ac_age: " << time_out(ac_age); + } + cerr << endl; + } + timeval pen_mod(new_mod); + timeval stain_ac(new_ac); + if (penalty) { + pen_mod = now; + pen_mod.tv_sec += penalty; + } + if (stain) { + stain_ac = now; + stain_ac.tv_sec -= stain; + } + timeval upd[2] = { +// beware: access illogically comes *before* modification here: + stain_ac, + pen_mod + }; + if (utimes(ipname.c_str(), upd)) + cerr << "oops" << endl; +} + +int whatsit::doit(const int penalty, const int stain){ + + if (!ipvar) { + cerr << progid + << " TCPREMOTEIP not set???" << endl; + // should never happen + // although you can make it happen using a weird test-harness + return(ex_syserr); + } + +// see if our directory exists: + struct stat dirstat; + int rslt = stat(dirname.c_str(), &dirstat); + if (rslt != 0){ + if (errno != ENOENT) { + cerr << progid << ": stat failed for '" + << dirname << "' : "; + perror(0); + } + rslt = mkdir(dirname.c_str(), 0755); + if (rslt != 0) { + cerr << progid + << "uid " << getuid() + << ": mkdir failed for '" + << dirname << "' : "; + perror(0); + return(ex_syserr); + } + } + + ipname = dirname + "/" + ipbase; + struct stat ipstat; + rslt = stat(ipname.c_str(), &ipstat); + if (rslt != 0){ + if (errno != ENOENT) { + cerr << progid << ": stat failed for '" + << ipname << "' : "; + perror(0); + } + ofstream foo; + int fd = creat(ipname.c_str(), 0644); + if (fd < 0){ + cerr << progid << ": create failed for '" + << ipname << "' : "; + perror(0); + } + close(fd); + update("new customer", now, now, penalty, stain); + return(ex_greylisting); + } + +// 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, stain); + return(ex_penaltybox); + } + if (mod_age < ac_age){ +// when he comes out on parole, he starts over with no reputation: + update("paroled spammer", now, now, penalty, stain); + return(ex_greylisting); + } + if (mod_age < minimum_age) { + update("early bird", mod_orig, now, penalty, stain); + 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, stain); + return(ex_greylisting); + } + if (ac_age > maximum_age) { + update("too old, starting over", now, now, penalty, stain); + return(ex_greylisting); + } +// if all checks are passed, must be OK: + update("returning customer", mod_orig, now, penalty, stain); + return 0; +} + +typedef vector VU; + +class VUx{ +public: + VU addr; + sa_family_t fam; + string str(); +}; + +string VUx::str(){ + char msgbuf[INET6_ADDRSTRLEN]; + const char* rslt = inet_ntop(fam, &addr[0], + msgbuf, sizeof(msgbuf)); + if (!rslt) rslt = ""; + return rslt; +} + +VUx parse_sockaddr(const sockaddr* ai_addr) { + void* numericAddress; + VUx rslt; + int addrsize; + rslt.addr = VU(0); + rslt.fam = ((sockaddr *)ai_addr)->sa_family; + switch (rslt.fam) { + case AF_INET: + numericAddress = &(((sockaddr_in *)ai_addr)->sin_addr.s_addr); + addrsize = sizeof(in_addr); + break; + case AF_INET6: + numericAddress = &(((sockaddr_in6 *)ai_addr)->sin6_addr.s6_addr); + addrsize = sizeof(in6_addr); + break; + default: + cerr << "?Unknown address family " << rslt.fam << endl; + return rslt; + } + unsigned char* foo = (unsigned char*) numericAddress; + rslt.addr = VU(foo, foo+addrsize); + return rslt; +} + +int diff(const VU aaa, const VU bbb){ + if(aaa.size() != bbb.size()) return 1; + for (unsigned int ii=0; ii < aaa.size(); ii++){ + if (aaa[ii] != bbb[ii]) return 1; + } + return 0; +} + +int whatsit::check_dns(){ + string addr("()"), host("()"); + vector checked; + int sts = check_dns_sub(addr, host, checked); + if (sts == 0) return sts; + if (sts != ex_badDNS) return sts; // possible ex_syserr +#if 1 + sts = 0; // demote badDNS to just a warning +#endif + cerr << progid; + if (!sts) cerr << " (warning)"; + cerr << " DNS inconsistency: " + << addr << " --> " + << host << " ==>"; + if (!checked.size()) cerr << " ()"; + else for (vector::const_iterator chk = checked.begin(); + chk != checked.end(); chk++) cerr << " " << *chk; + cerr << endl; + + return sts; +} + +int whatsit::check_dns_sub(string &addr, string &host, vector &checked){ + + struct addrinfo *result; + struct addrinfo *ipresult; + struct addrinfo *res; + addrinfo hints; + int error; + + memset(&hints, 0, sizeof(struct addrinfo)); +#if 1 + // restrict to TCP only; otherwise we get N records per address + hints.ai_protocol = IPPROTO_TCP; +#endif + + error = getaddrinfo(ipvar, NULL, &hints, &ipresult); + if (error == EAI_NONAME) return ex_badDNS; + if (error) { // some unexpected error + cerr << progid + << " odd error " << error + << " in getaddrinfo for " << ipvar + << " : " << gai_strerror(error) << endl; + return ex_syserr; + } + if (!ipresult) { + cerr << "should never happen (addr with no addrs?)" << endl; + return ex_syserr; + } + VUx ipAddr = parse_sockaddr(ipresult->ai_addr); + addr = ipAddr.str(); + + char* hostvar = getenv("TCPREMOTEHOST"); + if (hostvar) host = hostvar; + else return(ex_badDNS); + + error = getaddrinfo(hostvar, NULL, &hints, &result); + if (error == EAI_NONAME) return ex_badDNS; + if (error) { + cerr << progid + << " error " << error + << " compare " << EAI_NONAME + << " in getaddrinfo for " << ipvar + << " :: " << gai_strerror(error) << endl; + return ex_syserr; + } + +// loop over all returned results and check for a match. + for (res = result; res != NULL; res = res->ai_next){ + VUx hostAddr = parse_sockaddr(res->ai_addr); + checked.push_back(hostAddr.str()); + if (!diff(hostAddr.addr, ipAddr.addr)) { + ///// cerr << "match! " << ipAddr.addr.size() << endl; + goto done; + } + } + return ex_badDNS; + +done: + return 0; +} diff --git a/tools/libltgrey.h b/tools/libltgrey.h new file mode 100644 index 0000000..585ec01 --- /dev/null +++ b/tools/libltgrey.h @@ -0,0 +1,38 @@ +#include +#include /* for gettimeofday(), timeval */ +#include + +class whatsit{ +public: + std::string dirname; + std::string progname; + pid_t mypid; + timeval now; + char* ipvar; + char* hostvar; + std::string ipbase; + std::string ipname; + std::string hostname; + int mod_age; + int ac_age; + std::string suffix; + std::string progid; + int verbosity; + + whatsit(const std::string name, const std::string _dirname) + : dirname(_dirname), progname(name), mypid(getpid()), + mod_age(0), ac_age(0), + verbosity(0) + { + gettimeofday(&now, NULL); + } + int doit(const int penalty, const int stain); +// access comes after modification: + void update(const std::string msg, const timeval new_mod, + const timeval new_ac, const int penalty, const int stain); + int setup(); + int check_dns(); + int check_dns_sub(std::string &addr, std::string &host, + std::vector &checked); + void dump(const std::string var); +}; diff --git a/tools/ltgrey.c b/tools/ltgrey.c new file mode 100644 index 0000000..afdb4c1 --- /dev/null +++ b/tools/ltgrey.c @@ -0,0 +1,153 @@ +#include +#include /* for exit(), atoi() */ + +#include "libltgrey.h" +#include "utils.h" +#include "qq_exit_codes.h" + +using namespace std; +pid_t mypid; +string progname; + +#define exeunt exit + +// forward reference: +void scan(const string progid, const string p, const int copies=1); + +int main(int _argc, char** _argv){ + mypid = getpid(); + int argc(_argc); + char** argv(_argv); + const string dirname("/var/qmail/greylist"); + whatsit foo(argv[0], dirname); argc--; argv++; + int scanmode(0); + int copies(1); + int penalty(0); + int stain(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){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + penalty = atoi(*argv++); argc--; + } else if (prefix(arg, "-stain")) { + if (!argc){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + stain = atoi(*argv++); argc--; + } else if (prefix(arg, "-suffix")) { + if (!argc){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + foo.suffix += *argv++; argc--; + } else { + cerr << "Unrecognized arg: " << arg << endl; + exeunt(ex_syserr); + } + } + if (foo.setup()) return ex_syserr; + + if (scanmode) { + scan(foo.progid, dirname, copies); + return 0; + } + + int sts = foo.doit(penalty, stain); + if (sts == ex_syserr) return sts; + if (!check) return ex_good; + +// check mode ... 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 + + int dns = foo.check_dns(); + if (dns == ex_syserr || dns == ex_spam) return dns; + exeunt(sts); +} + +////////////////////////////////////////////////////////////////////// +// requires apt-get install libboost-filesystem-dev: +#include +#include +#include /* for stat(), getaddrinfo() */ +#include /* for stat() */ +#include /* for stat() */ +#include /* for perror */ +#include + +const int minute(60); +const int hour(60*minute); +const int day(24*hour); + +const int minimum_age(15*minute); +const int maximum_age(32*day); +const int probation(4*hour); + +void scan(const string progid, const string p, const int copies){ + timeval now; + gettimeofday(&now, NULL); + using namespace boost::filesystem; + + if (is_directory(p)) { + for (directory_iterator itr(p); itr!=directory_iterator(); ++itr) { + string basename = itr->path().filename(); + for (int ii = 0; ii < copies; ii++) + cout << setw(20) << left << basename << ' '; // display filename only + if (is_regular_file(itr->status())) { +// cout << " [" << file_size(itr->path()) << ']'; + struct stat mystat; + string fn = p + "/" + basename; + int rslt = stat(fn.c_str(), &mystat); + if (rslt != 0){ + cerr << progid << ": stat failed for '" + << fn << "' : "; + perror(0); + } + int mod_age = now.tv_sec - mystat.st_mtime; + int ac_age = now.tv_sec - mystat.st_atime; + cout << setw(10) << time_out(mod_age) + << " " << setw(10) << time_out(ac_age); + if (0) { + + } else if (mod_age < 0) { + cout << " penalty"; + } else if (mod_age < ac_age) { + cout << " parole"; + } else if (mod_age - ac_age < minimum_age // early bird, or completely unused + && mod_age > probation) { // did not diligently resubmit + cout << " disprobation"; + 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) { + cout << " expired"; + } else { + cout << " OK"; + } + } + cout << '\n'; + } + } + else { + // starting point is not a directory: + cout << (exists(p) ? "Found: " : "Not found: ") << p << '\n'; + } +} diff --git a/tools/makefile b/tools/makefile index 76df23b..f0a3f70 100644 --- a/tools/makefile +++ b/tools/makefile @@ -15,10 +15,10 @@ qmain = pido.c hi-q.c skrewt.c hi-test.c mail-scan.c greylist.c wripper.c qprogs = $(qmain:%.c=%) # sources for other main programs: -moremain = wripper.c bash-c.c +moremain = wripper.c bash-c.c ltgrey.c moreprogs = $(moremain:%.c=%) -nonmain = +nonmain = libltgrey.c sources = $(qmain) $(moremain) $(nonmain) @@ -37,10 +37,14 @@ all: $(qprogs) $(moreprogs) show: : --- $(qprogs) +++ $(moreprogs) -greylist: greylist.o +skrewt: skrewt.o $(CC) $< -lboost_filesystem-mt -lboost_system -o $@ -# $(CC) $< -lboost_filesystem -o $@ +greylist: greylist.o utils.o + $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ + +ltgrey: ltgrey.o utils.o libltgrey.o + $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ wripper: wripper.o $(CC) $< -o $@ diff --git a/tools/qq_exit_codes.h b/tools/qq_exit_codes.h new file mode 100644 index 0000000..2af6848 --- /dev/null +++ b/tools/qq_exit_codes.h @@ -0,0 +1,15 @@ +// error exit codes, mostly as stated in qmail.c +#define qq_exit_codes \ +foo(good, 0) ;\ +foo(spam, 21) ;\ +foo(penaltybox, 22) ;\ +foo(badDNS, 23) ;\ +foo(usage, 39) ;\ +foo(greylisting, 70) ;\ +foo(syserr, 71) ;\ +foo(comerr, 74) ; + +// expand the codes to make some names: +#define foo(name, num) const int ex_ ## name = num +qq_exit_codes +#undef foo diff --git a/tools/skrewt.c b/tools/skrewt.c index a43fd13..3fee644 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -37,19 +37,7 @@ void usage(const int sts){ exit(sts); } -// error exit codes, mostly as stated in qmail.c -#define ErrorCodes \ -foo(good, 0) ;\ -foo(spam, 21) ;\ -foo(permerr, 31) ;\ -foo(usage, 39) ;\ -foo(greylisting, 70) ;\ -foo(syserr, 71) ;\ -foo(comerr, 74) ; - -#define foo(name, num) const int ex_ ## name = num -ErrorCodes -#undef foo +#include "qq_exit_codes.h" ///////////////////////////////////////////////////////// @@ -316,8 +304,8 @@ int main(int _argc, const char** _argv){ headrec += "\n" + noCR(line); } // here with a fully assembled header record +// headrec (unlike line) contains no DOS CR characters int len = headrec.length(); - if (len && headrec[len-1] == '\r') len--; // reduced length, not counting if (len == 0) { saw_blank_line = 1; break; // no more headers in this message @@ -351,6 +339,7 @@ int main(int _argc, const char** _argv){ if (0) if (recno <= 6) cerr << progid << "#" << recno << " " << headrec << endl; } + if (saw_blank_line) {/* ignore */} cerr << progid <<" Mid '" << message_id << "'" << endl; // Headers are done. diff --git a/tools/utils.c b/tools/utils.c new file mode 100644 index 0000000..3ec6e4c --- /dev/null +++ b/tools/utils.c @@ -0,0 +1,44 @@ +#include +#include +#include +//#include /* for abs() */ +#include + +// strip off the directory part of a path, leaving just +// the basic filename +std::string basename(const std::string path){ + size_t where = path.rfind("/"); + if (where != std::string::npos) return path.substr(1+where); + return path; +} + +//////////////// +// little utility to help with argument parsing: +// +int prefix(const std::string shorter, const std::string longer){ + return shorter == longer.substr(0, shorter.length()); +} + +/////////////// +// print a time as (-)hh:mm:ss +// +std::string time_out(const int _ttt){ +using namespace std; + int ttt(abs(_ttt)); + int sec(ttt % 60); + int min((ttt / 60) % 60); + int hr(ttt / 3600); + stringstream foo; + int didsome(0); + if (_ttt < 0) foo << "-"; + if (hr) { + foo << hr << ":"; + didsome++; + } + if (didsome || min){ + foo << setw(didsome?2:1) << setfill('0') << min << ":"; + didsome++; + } + foo << setw(didsome?2:1) << setfill('0') << sec; + return foo.str(); +} diff --git a/tools/utils.h b/tools/utils.h new file mode 100644 index 0000000..450db85 --- /dev/null +++ b/tools/utils.h @@ -0,0 +1,3 @@ +std::string basename(const std::string path); +int prefix(const std::string shorter, const std::string longer); +std::string time_out(const int _ttt); -- cgit v1.2.3 From 63be414b62f3234ad80607b95e8e71e33bfd8025 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 17:00:15 -0700 Subject: move more stuff to utils.c ... I hate duplication of code --- tools/hi-test.c | 14 +------------- tools/mail-scan.c | 26 +------------------------- tools/makefile | 13 ++++++++----- tools/skrewt.c | 32 +------------------------------- tools/utils.c | 16 ++++++++++++++++ tools/utils.h | 3 +++ 6 files changed, 30 insertions(+), 74 deletions(-) (limited to 'tools/makefile') diff --git a/tools/hi-test.c b/tools/hi-test.c index 0661ada..cd0152c 100644 --- a/tools/hi-test.c +++ b/tools/hi-test.c @@ -6,6 +6,7 @@ #include #include /* perror() */ +#include "utils.h" using namespace std; @@ -16,13 +17,6 @@ const int sa_usage(64); int verbosity(0); -//////////////// -// little utility to help with argument parsing: -// -int prefix(const string shorter, const string longer){ - return shorter == longer.substr(0, shorter.length()); -} - void exeunt(const int sts){ if (sts == sa_good) exit(sts); @@ -69,12 +63,6 @@ void countsome(const int unit){ << " read " << total << " bytes from unit " << unit << endl; } -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, const char** _argv){ int snooze(0); int status(0); diff --git a/tools/mail-scan.c b/tools/mail-scan.c index dc8aa5c..b0c4137 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -31,7 +31,7 @@ #include /* perror */ #include -////#include +#include "utils.h" using namespace std; @@ -105,30 +105,6 @@ int cmp_casefold(const std::string& a, const std::string& b) { return 0; } - -string toLower(const std::string& a){ - string rslt = a; - string::iterator rr; - for (rr = rslt.begin(); rr != rslt.end(); rr++){ - *rr = tolower(*rr); - } - return rslt; -} - -//////////////// -string ltrim(string foo){ - size_t where = foo.find_first_not_of(" \t\r\n"); - if (where == foo.npos) return foo; - return foo.substr(where); -} - -//////////////// -// little utility to help with argument parsing: -// -int prefix(const string shorter, const string longer){ - return shorter == longer.substr(0, shorter.length()); -} - void exeunt(const int sts){ if (sts == sa_good) exit(sts); diff --git a/tools/makefile b/tools/makefile index f0a3f70..6594ca8 100644 --- a/tools/makefile +++ b/tools/makefile @@ -37,8 +37,8 @@ all: $(qprogs) $(moreprogs) show: : --- $(qprogs) +++ $(moreprogs) -skrewt: skrewt.o - $(CC) $< -lboost_filesystem-mt -lboost_system -o $@ +skrewt: skrewt.o utils.o + $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ greylist: greylist.o utils.o $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ @@ -47,11 +47,14 @@ ltgrey: ltgrey.o utils.o libltgrey.o $(CC) $^ -lboost_filesystem-mt -lboost_system -o $@ wripper: wripper.o - $(CC) $< -o $@ + $(CC) $^ -o $@ chgrp daemon $@ && chmod g+s $@ || true -mail-scan: mail-scan.o - $(CC) $< -lboost_regex -o $@ +mail-scan: mail-scan.o utils.o + $(CC) $^ -lboost_regex -o $@ + +hi-test: hi-test.o utils.o + $(CC) $^ -lboost_regex -o $@ install: install $(qprogs) /var/qmail/bin/ diff --git a/tools/skrewt.c b/tools/skrewt.c index 3fee644..6749a01 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -38,7 +38,7 @@ void usage(const int sts){ } #include "qq_exit_codes.h" - +#include "utils.h" ///////////////////////////////////////////////////////// // Case insensitive comparison of strings @@ -83,23 +83,6 @@ int cmp_casefold(const std::string& a, const std::string& b) { return 0; } - -string toLower(const std::string& a){ - string rslt = a; - string::iterator rr; - for (rr = rslt.begin(); rr != rslt.end(); rr++){ - *rr = tolower(*rr); - } - return rslt; -} - -//////////////// -string ltrim(const string foo){ - size_t where = foo.find_first_not_of(" \t\r\n"); - if (where == foo.npos) return foo; - return foo.substr(where); -} - string noCR(const string bar){ string foo(bar); int len = foo.length(); @@ -111,13 +94,6 @@ string noCR(const string bar){ return foo; } -//////////////// -// little utility to help with argument parsing: -// -int prefix(const string shorter, const string longer){ - return shorter == longer.substr(0, shorter.length()); -} - void maybe_exeunt(const int sts, const int really){ if (!really) return; if (sts == ex_good) exit(sts); @@ -142,12 +118,6 @@ void exeunt(const int sts){ maybe_exeunt(sts, 1); } -string basename(const string path){ - size_t where = path.rfind("/"); - if (where != string::npos) return path.substr(1+where); - return path; -} - string progname, progid; int mypid; diff --git a/tools/utils.c b/tools/utils.c index 3ec6e4c..aecbfda 100644 --- a/tools/utils.c +++ b/tools/utils.c @@ -42,3 +42,19 @@ using namespace std; foo << setw(didsome?2:1) << setfill('0') << sec; return foo.str(); } + +std::string toLower(const std::string a){ + std::string rslt = a; + std::string::iterator rr; + for (rr = rslt.begin(); rr != rslt.end(); rr++){ + *rr = tolower(*rr); + } + return rslt; +} + +//////////////// +std::string ltrim(const std::string foo){ + size_t where = foo.find_first_not_of(" \t\r\n"); + if (where == foo.npos) return foo; + return foo.substr(where); +} diff --git a/tools/utils.h b/tools/utils.h index 450db85..ec467c6 100644 --- a/tools/utils.h +++ b/tools/utils.h @@ -1,3 +1,6 @@ std::string basename(const std::string path); int prefix(const std::string shorter, const std::string longer); std::string time_out(const int _ttt); + +std::string toLower(const std::string a); +std::string ltrim(const std::string a); -- cgit v1.2.3