From 6cdc5c9d9caecba0e13264931824a9ac3cd3f738 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 15:26:53 -0700 Subject: nuisance --- tools/mail-scan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/mail-scan.c b/tools/mail-scan.c index 3749945..bb8605f 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -303,8 +303,7 @@ int main(int _argc, const char** _argv){ } } if (vflag && !foundsome_infile) { - cout << foundsome_infile - << " ... " << *file << endl; + cout << *file << endl; didprint++; } if (group_flag && didprint) cout << endl; -- cgit v1.2.3 From 1427af73b6837df40333f3b317e9ce1f5fde83cc Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 07:02:45 -0700 Subject: remove bad stuff from mailbox, sa-learn it, move it to ../spam/ --- tools/mail-zap | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 tools/mail-zap diff --git a/tools/mail-zap b/tools/mail-zap new file mode 100755 index 0000000..912d740 --- /dev/null +++ b/tools/mail-zap @@ -0,0 +1,13 @@ +#! /bin/bash + +if test -z "$*" ; then + exit +fi + +mail-scan +subject "$@" + +sa-learn --spam "$@" + +for file in "$@" ; do + mv "$file" "$(dirname $file)/../spam/" +done -- cgit v1.2.3 From d074f40718110a3b289dff5fb594425431805df8 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 07:04:19 -0700 Subject: show filename only once in "-l" mode --- tools/mail-scan.c | 60 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/tools/mail-scan.c b/tools/mail-scan.c index bb8605f..0d7f722 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -168,6 +168,7 @@ public: } else { key = init; } + //xx cerr << "watcher key: " << key << " val: " << val << endl; } }; @@ -240,7 +241,7 @@ int main(int _argc, const char** _argv){ string boundary("x-xx-x"); int msgsize(0); int foundsome_infile(0); - for (;;){ // loop over all records in file + for (;;){ // loop over all records in this file if (inheads) { list Header; string line; @@ -264,44 +265,45 @@ int main(int _argc, const char** _argv){ } if (Header.front().length() == 0) { inheads = 0; + continue; // blank line needs no further processing + } + string headword; // the first thing on the line, e.g. "Subject" + string rest; + string header; + if (!multi) header = join(" ", Header); + else header = join("\n", Header); + size_t where = header.find(":"); + if (where != string::npos) { + headword = header.substr(0, where); + rest = ltrim(header.substr(1+where)); } - else { - string headword; // the first thing on the line, e.g. "Subject" - string rest; - string header; - if (!multi) header = join(" ", Header); - else header = join("\n", Header); - size_t where = header.find(":"); - if (where != string::npos) { - headword = header.substr(0, where); - rest = ltrim(header.substr(1+where)); - } // temporary? FIXME? lowercase - headword = toLower(headword); - rest = toLower(rest); - for (list::const_iterator ptr = watchword.begin(); - ptr != watchword.end(); ptr++) { - if (headword == toLower(ptr->key)) { - // here if match as to keyword; check for match as to value - if (ptr->val.length()==0 - || rest.find(toLower(ptr->val)) != string::npos) { - foundsome_infile++; - if (!vflag) { - // << foundsome_infile << " " ; (number of occurrences) - cout << *file; - if (!fname_only) cout << " :: " << header; - cout << endl; - didprint++; - } + headword = toLower(headword); + rest = toLower(rest); + for (list::const_iterator ptr = watchword.begin(); + ptr != watchword.end(); ptr++) { + if (headword == toLower(ptr->key)) { + // here if match as to keyword; check for match as to value + if (ptr->val.length()==0 + || rest.find(toLower(ptr->val)) != string::npos) { + foundsome_infile++; + if (!vflag) { + // << foundsome_infile << " " ; (number of occurrences) + cout << *file; + if (!fname_only) cout << " :: " << header; + cout << endl; + didprint++; } } } } +// only show file once, even if there might have been multiple matches: + if (fname_only && didprint) break; } else { // not in header break; } - } + } // end loop over matching records in this file if (vflag && !foundsome_infile) { cout << *file << endl; didprint++; -- cgit v1.2.3 From 0d63ba5de5370191e9463f8fc5ccb6223c23c185 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 12:55:28 -0700 Subject: minor clarification of log msg --- qmail-send.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmail-send.c b/qmail-send.c index b01dd41..1646a5d 100644 --- a/qmail-send.c +++ b/qmail-send.c @@ -1362,7 +1362,7 @@ fd_set *rfds; log2("info msg ",strnum3); strnum2[fmt_ulong(strnum2,(unsigned long) st.st_size)] = 0; log2(": bytes ",strnum2); - log1(" from <"); logsafe(todoline.s + 1); + log1(" envelope_from <"); logsafe(todoline.s + 1); strnum2[fmt_ulong(strnum2,pid)] = 0; log2("> qp ",strnum2); strnum2[fmt_ulong(strnum2,uid)] = 0; -- cgit v1.2.3 From 40c8c828972af044b7628b2c167093f78353fcdd Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 13:05:15 -0700 Subject: fix commented-out bug; clarify variable-name --- tools/hi-q.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 7215b0c..237dbd5 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -424,7 +424,7 @@ int main(int argc, char** argv) { cerr << endl; } - pid_t special_kid = kidpid[nkids-1]; + pid_t special_pid = kidpid[nkids-1]; int alive(nkids-1); // not counting the special kid int best_blame(0); // best reason, even if not a great reason pid_t argbest_blame(-1); // kid# associated with best blame @@ -432,7 +432,7 @@ int main(int argc, char** argv) { for (;;) { if (alive == 0) break; pid_t somekid = waitpid(-1, &kidstatus, WUNTRACED); - if (somekid == special_kid){ + if (somekid == special_pid){ // do not decrement the "alive" counter // since that only applies to non-special kids if (WIFEXITED(kidstatus)) { @@ -510,13 +510,8 @@ int main(int argc, char** argv) { // now that the envelope information has been transfered, // wait for the last kid in the usual way { - -#ifdef moretesting -fprintf(stderr, "About to wait for kid #%d (%d)\n", - special_kid, kidpid[special_kid]); -#endif for(;;) { - waitpid(special_kid, &kidstatus, WUNTRACED); + waitpid(special_pid, &kidstatus, WUNTRACED); if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); cerr << "hi-q ends with status: " << sts << endl; -- cgit v1.2.3 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/mail-scan.c | 51 ++++++++++++++++++++++++++++++++++++++++++--------- tools/makefile | 3 +++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/tools/mail-scan.c b/tools/mail-scan.c index 0d7f722..9e79ff9 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -13,6 +13,12 @@ // Then: // grep score=[34] /home/user/Maildir/new/* -l | xargs mv-to -i /home/user/Maildir/spam/ +///////////// +// Another hint: using the "-addr" feature: +// grep score=[234] /home/user/Maildir/spam/* -l | \... +// xargs mail-scan +from -addr | sort | uniq -c | sort -nr | head | blacklist-update -bare + + #include #include /* for exit() */ #include @@ -25,6 +31,8 @@ #include /* stat() */ #include /* perror */ +#include +////#include using namespace std; @@ -157,6 +165,8 @@ class watcher { public: string key; string val; + boost::regex valrx; + boost::regex keyrx; watcher(const string init) : key(""), val("") @@ -168,6 +178,8 @@ public: } else { key = init; } + keyrx = boost::regex(key, boost::regex_constants::icase); + valrx = boost::regex(val, boost::regex_constants::icase); //xx cerr << "watcher key: " << key << " val: " << val << endl; } }; @@ -189,6 +201,8 @@ int main(int _argc, const char** _argv){ int group_flag(0); int multi(0); int fname_only(0); + int addr_mode(0); + boost::regex addr_filter(string("<.*@(.*)>"), boost::regex_constants::icase); while (argc) { string arg(*argv); argv++; argc--; @@ -196,16 +210,19 @@ int main(int _argc, const char** _argv){ if (prefix(arg, "-help")) { usage(0); } - if (prefix(arg, "-vert")){ + if (prefix(arg, "-vert" /* short */)){ vflag++; continue; - } if (prefix(arg, "-group")){ + } if (prefix(arg, "-group" /* short */)){ group_flag++; continue; - } else if (prefix(arg, "-l")){ + } else if (prefix(arg, "-l" /* short */)){ fname_only++; continue; - } else if (prefix(arg, "-multi")){ + } else if (prefix(arg, "-address" /* long */)){ + addr_mode++; + continue; + } else if (prefix(arg, "-multi" /* long */)){ multi++; continue; } else if (arg.substr(0,1) == "-") { @@ -288,11 +305,27 @@ int main(int _argc, const char** _argv){ || rest.find(toLower(ptr->val)) != string::npos) { foundsome_infile++; if (!vflag) { - // << foundsome_infile << " " ; (number of occurrences) - cout << *file; - if (!fname_only) cout << " :: " << header; - cout << endl; - didprint++; + if (!addr_mode){ + // << foundsome_infile << " " ; (number of occurrences) + cout << *file; + if (!fname_only) { + cout << " :: " << header; + } + cout << endl; + didprint++; + } else /* addr_mode */{ + boost::smatch matches; +//// boost::match_flag_type flg; +//// boost::regex_search(header, matches, addr_filter); +//// boost::regex_search(header.begin(), header.end(), matches, addr_filter, flg); +/// boost::regex_search(header, matches, addr_filter); + if (boost::regex_search(header, matches, addr_filter)){ + cout << string(matches[1].first, matches[1].second) << endl; + } else { + cerr << "no match, ignoring: " << header << endl; + } + didprint++; + } } } } 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 58b88adfe892e5a327148ad7a991d453ba231607 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 15:17:17 -0700 Subject: implement regular expressions --- tools/mail-scan.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/mail-scan.c b/tools/mail-scan.c index 9e79ff9..1ea245f 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -15,9 +15,8 @@ ///////////// // Another hint: using the "-addr" feature: -// grep score=[234] /home/user/Maildir/spam/* -l | \... -// xargs mail-scan +from -addr | sort | uniq -c | sort -nr | head | blacklist-update -bare - +// mail-scan +x-spam.*:score=[234] /home/jean/Maildir/spam/* -l | \.... +// xargs mail-scan +from -addr | sort | uniq -c | sort -nr | head -20 #include #include /* for exit() */ @@ -294,15 +293,14 @@ int main(int _argc, const char** _argv){ headword = header.substr(0, where); rest = ltrim(header.substr(1+where)); } -// temporary? FIXME? lowercase - headword = toLower(headword); - rest = toLower(rest); + for (list::const_iterator ptr = watchword.begin(); ptr != watchword.end(); ptr++) { - if (headword == toLower(ptr->key)) { +// regex_match not regex_search ... keyrx must match *whole* headword + if (boost::regex_match(headword, ptr->keyrx)){ // here if match as to keyword; check for match as to value if (ptr->val.length()==0 - || rest.find(toLower(ptr->val)) != string::npos) { + || boost::regex_search(rest, ptr->valrx)){ foundsome_infile++; if (!vflag) { if (!addr_mode){ -- cgit v1.2.3 From c906e5919f7730515669f9847c8a61585a8eea6b Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 15:17:54 -0700 Subject: simple thing to calculate percentiles --- tools/percentile.pl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 tools/percentile.pl diff --git a/tools/percentile.pl b/tools/percentile.pl new file mode 100755 index 0000000..37e6068 --- /dev/null +++ b/tools/percentile.pl @@ -0,0 +1,39 @@ +#! /usr/bin/perl -w + +use strict; +use Symbol; + +main: { + my @buf = (); + my $running = 0; + my $file = $ARGV[0] + || die "usage: $0 filename\n"; + my $inch = Symbol::gensym; + open($inch, '<', $file) + || die "Could not open input file '$file'\n"; + while (my $line = <$inch>) { + chomp $line; + push @buf, $line; + my @stuff = split(' ', $line); + $running += $stuff[0]; + } + close $inch; + my $total = $running; + $running = 0; + open($inch, '<', $file) + || die "Could not reopen input file '$file'\n"; + while (my $line = <$inch>) { + chomp $line; + push @buf, $line; + my @stuff = split(' ', $line); + $running += $stuff[0]; + my $frac = 100 * $running / $total; + if (0) { + printf("%5d %5d %5.1f %s\n", + $stuff[0], $running, $frac, $stuff[1]); + } else { + print 'blacklist_from *@', $stuff[1], "\n"; + } + } + +} -- cgit v1.2.3 From da0e79048c01344bc430833ef989469f52dd3f3d Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 17 Jul 2012 15:20:16 -0700 Subject: trivial --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f0e0ff6..b37624b 100644 --- a/.gitignore +++ b/.gitignore @@ -147,4 +147,7 @@ ucspi-tcp-0.88/who@ checkpasswd/auto_home.c checkpasswd/checkpassword checkpasswd/choose -checkpasswd/hasuserpw.h \ No newline at end of file +checkpasswd/hasuserpw.h +skrewt +mail-scan +hi-test -- cgit v1.2.3 From b02174df1578e5d869001446815f461fb48589aa Mon Sep 17 00:00:00 2001 From: John Denker Date: Wed, 18 Jul 2012 04:58:36 -0700 Subject: minor upgrade to log/progress msg --- tools/hi-q.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 237dbd5..81e717a 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -514,11 +514,16 @@ int main(int argc, char** argv) { waitpid(special_pid, &kidstatus, WUNTRACED); if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); - cerr << "hi-q ends with status: " << sts << endl; + cerr << "hi-q says: qq program " << kidpid[nkids-1] + << " i.e. '" << filter[nkids-1][0] << "'" + << " returned status " << sts + << endl; return sts; } else if (WIFSIGNALED(kidstatus)) { - cerr << "hi-q: special kid was killed by signal " - << WTERMSIG(kidstatus) << endl; + cerr << "hi-q says: qq program " << kidpid[nkids-1] + << " i.e. '" << filter[nkids-1][0] << "'" + << " was killed by signal " << WTERMSIG(kidstatus) + << endl; return ex_syserr; } else { /* paused, not dead */ -- cgit v1.2.3 From f426b21b6ab89d1e70fb4147ccb42fbd31b34969 Mon Sep 17 00:00:00 2001 From: John Denker Date: Wed, 18 Jul 2012 12:37:10 -0700 Subject: add "-r" (reverse) mode to mail-zap --- tools/mail-zap | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tools/mail-zap b/tools/mail-zap index 912d740..4467ef6 100755 --- a/tools/mail-zap +++ b/tools/mail-zap @@ -1,13 +1,26 @@ #! /bin/bash -if test -z "$*" ; then +todo="" +verb="--spam" +dest="spam" + +for arg in "$@" ; do + case "$arg" in + -r) verb="--forget" + dest="new" + ;; + *) todo="$todo $arg" + esac +done + +if test -z "$todo" ; then exit fi -mail-scan +subject "$@" +mail-scan +subject $todo -sa-learn --spam "$@" +sa-learn $verb $todo -for file in "$@" ; do - mv "$file" "$(dirname $file)/../spam/" +for file in $todo ; do + mv "$file" "$(dirname $file)/../$dest/" done -- cgit v1.2.3 From 1a6f4a654368174dc717d00b9eaf06872915d51c Mon Sep 17 00:00:00 2001 From: John Denker Date: Wed, 18 Jul 2012 04:58:53 -0700 Subject: add dummy-mta debian package --- .gitignore | 4 ++++ dummy-mta/control | 16 ++++++++++++++++ dummy-mta/debian-binary | 1 + dummy-mta/makefile | 13 +++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 dummy-mta/control create mode 100644 dummy-mta/debian-binary create mode 100644 dummy-mta/makefile diff --git a/.gitignore b/.gitignore index b37624b..3561ee3 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,7 @@ checkpasswd/hasuserpw.h skrewt mail-scan hi-test + +control.tar.gz +data.tar.gz +dummy-mail-transfer-agent_all.deb diff --git a/dummy-mta/control b/dummy-mta/control new file mode 100644 index 0000000..0b5336d --- /dev/null +++ b/dummy-mta/control @@ -0,0 +1,16 @@ +Package: dummy-mail-transfer-agent +Provides: mail-transport-agent +Conflicts: mail-transport-agent +Version: 1 +Architecture: all +Maintainer: nobody +Installed-Size: 0 +Depends: +Section: universe/mail +Priority: extra +Description: Tells the package management system /not/ to go looking + for a Mail Transfer Agent such as exim or ssmtp. This is useful if + you plan to install a proper MTA later and don't want broken + depenencies in the meantime. It is also useful if you have installed + by hand something that provides MTA functionality, and you don't want + to bother making a .deb package out of it. diff --git a/dummy-mta/debian-binary b/dummy-mta/debian-binary new file mode 100644 index 0000000..cd5ac03 --- /dev/null +++ b/dummy-mta/debian-binary @@ -0,0 +1 @@ +2.0 diff --git a/dummy-mta/makefile b/dummy-mta/makefile new file mode 100644 index 0000000..f9e0cf5 --- /dev/null +++ b/dummy-mta/makefile @@ -0,0 +1,13 @@ + + +dummy-mail-transfer-agent_all.deb : debian-binary control.tar.gz data.tar.gz + ar -r $@ $^ + +control.tar.gz : control + tar -c $^ | gzip > $@ + +data.tar.gz : + tar --exclude '*' -c nothing-at-all | gzip > $@ + +install : dummy-mail-transfer-agent_all.deb + dpkg -i $< -- 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(-) 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(-) 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 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 19fdf737cc5e6b535cd02c280fd788793e4f5f3a Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 15:48:24 -0700 Subject: teach qmail.c to support greylisting --- qmail.c | 1 + 1 file changed, 1 insertion(+) diff --git a/qmail.c b/qmail.c index 51883e1..476c788 100644 --- a/qmail.c +++ b/qmail.c @@ -122,6 +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 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)"; -- cgit v1.2.3 From 84688a05a4430daf8dedf80bce35286aff4f4b1c Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 17:24:43 -0700 Subject: bare beginnings of a greylisting system --- .gitignore | 3 ++ tools/filters.conf | 1 + tools/greylist.c | 89 +++++++++++++++++++++++++++++++++++++++++- tools/hi-q.c | 111 +++++++++++++++++++++++++++++------------------------ 4 files changed, 152 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 3561ee3..ca1ef6d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.orig *.rej *.logg +*#[0-9]* has?????.h auto-gid @@ -151,7 +152,9 @@ checkpasswd/hasuserpw.h skrewt mail-scan hi-test +greylist +auto_uids.c control.tar.gz data.tar.gz dummy-mail-transfer-agent_all.deb diff --git a/tools/filters.conf b/tools/filters.conf index 8bc2efe..641b792 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -1,4 +1,5 @@ # configuration file for hi-q black /var/qmail/bin/skrewt +gray /var/qmail/bin/greylist black /usr/local/bin/spamc -Y 0 -s 1000000 qq /var/qmail/bin/qmail-queue diff --git a/tools/greylist.c b/tools/greylist.c index fa7d701..8adac05 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -1,5 +1,92 @@ +#include /* for exit(), getenv() */ +#include +#include +#include /* for stat() */ +#include /* for stat() */ +#include /* for stat() */ +#include /* for perror */ +#include /* for ENOENT */ +#include /* for ofstream() */ +#include /* for creat() */ +using namespace std; -int main(){ +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); + +int main(int argc, char** argv){ + mypid = getpid(); + progname = argv[0]; +// dump("TCPREMOTEIP"); +// dump("TCPREMOTEHOST"); + + 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); + } + } + + string 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); + return(bug_bait_grey); + } else { + cerr << "file exists: " << ipname << endl; + } return 0; } diff --git a/tools/hi-q.c b/tools/hi-q.c index 2ddc448..21724a1 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -11,7 +11,7 @@ #include #include /* for exit(), getenv() */ -#include +#include /* for perror */ #include #include /* for fork(), wait() */ #include @@ -29,6 +29,7 @@ using namespace std; // error exit codes, mostly as stated in qmail.c const int ex_good = 0; const int ex_spam = 21; +const int ex_grey = 70; const int ex_syserr = 71; const int ex_comerr = 74; @@ -139,7 +140,7 @@ int xclose(int arg){ extern char** environ; -typedef enum {gray, black, qq, fail} moder; +typedef enum {grey, black, qq, fail} moder; class jobber{ public: @@ -161,8 +162,8 @@ public: void setmode(const string _mode) { if (0) {} - else if (_mode == "gray") mode = gray; - else if (_mode == "grey") mode = gray; // variant spelling + else if (_mode == "gray") mode = grey; + else if (_mode == "grey") mode = grey; // variant spelling else if (_mode == "black") mode = black; else if (_mode == "qq") mode = qq; else { @@ -175,8 +176,8 @@ public: int main(int argc, char** argv) { progname = *argv; mypid = getpid(); - dump("TCPREMOTEIP"); - dump("TCPREMOTEHOST"); +// dump("TCPREMOTEIP"); +// dump("TCPREMOTEHOST"); int verbose(0); int kidstatus; @@ -270,41 +271,44 @@ int main(int argc, char** argv) { // to close it and dup() something useful onto it. map iiofpid; - for (unsigned int ii=0; ii < nkids; ii++){ /* loop starting all kids */ - int datapipe[2]; - int kid_end; + for (unsigned int ii=0; ii < nkids; ii++){ /* loop starting all kids */ //xx fprintf(stderr, "Top of loop %d loose: %d\n", ii, loose_end); - if (loose_end) { - close(0); - dup2(loose_end, 0); - close(loose_end); - } + int kid_end; + if (filter[ii].mode != grey){ + int datapipe[2]; -// Create a pipe, which will be used to connect -// this child's fd1 to the next child's fd0 ... -// except for the last kid, which reads both fd0 and fd1, -// while writing nothing. + if (loose_end) { + close(0); + dup2(loose_end, 0); + close(loose_end); + } - rslt = pipe(datapipe); - if (rslt < 0) { - fprintf(stderr, "hi-q: could not create datapipe: "); - perror(0); - panic(ex_syserr); - } + // Create a pipe, which will be used to connect + // this child's fd1 to the next child's fd0 ... + // except for the last kid, which reads both fd0 and fd1, + // while writing nothing. -//xx fprintf(stderr, "pipe: %d %d\n", datapipe[0], datapipe[1]); + rslt = pipe(datapipe); + if (rslt < 0) { + fprintf(stderr, "hi-q: could not create datapipe: "); + perror(0); + panic(ex_syserr); + } -// For N-1 kids, the loose end feeds forward. -// It will be written by this kid and read by the next kid. -// For the last kid, the loose end connects to hi-q. -// It will be written by hi-q and read by the last kid. + //xx fprintf(stderr, "pipe: %d %d\n", datapipe[0], datapipe[1]); - int lastkid = (ii == nkids-1); -#define flip(a,b) (lastkid ? b : a) - loose_end = datapipe[flip(rEnd, wEnd)]; - kid_end = datapipe[flip(wEnd, rEnd)]; + // For N-1 kids, the loose end feeds forward. + // It will be written by this kid and read by the next kid. + // For the last kid, the loose end connects to hi-q. + // It will be written by hi-q and read by the last kid. + + int lastkid = (ii == nkids-1); + #define flip(a,b) (lastkid ? b : a) + loose_end = datapipe[flip(rEnd, wEnd)]; + kid_end = datapipe[flip(wEnd, rEnd)]; + } kidpid[ii] = fork(); if (kidpid[ii] == -1) { @@ -358,24 +362,23 @@ int main(int argc, char** argv) { } } -// Now that we are through creating pipes, we don't -// need to continue blocking fd1: - close(1); - - close(loose_end); // the reading end is none of this kid's business - // except last kid: writing end + if (filter[ii].mode != grey){ + close(loose_end); // the reading end is none of this kid's business + // except last kid: writing end + + // Note this does an implicit close on the previously-open fd1: + rslt = dup2(kid_end, 1); // the writing end is stdout for this kid + // except last kid: nonstandard input + if (rslt < 0) { + fprintf(stderr, "hi-q: kid %d: dup2(%d,1) failed: ", ii, kid_end); + perror(0); + exit(ex_syserr); + } - rslt = dup2(kid_end, 1); // the writing end is stdout for this kid - // except last kid: nonstandard input - if (rslt < 0) { - fprintf(stderr, "hi-q: kid %d: dup2(%d,1) failed: ", ii, kid_end); - perror(0); - exit(ex_syserr); + close(kid_end); // use fd1 instead now + // OK, at this point this kid is set up to read fd0 and write fd1 + // (except last kid reads fd1 as well as fd0). } - - close(kid_end); // use fd1 instead now - // OK, at this point this kid is set up to read fd0 and write fd1 - // (except last kid reads fd1 as well as fd0). //// probe_fd(); int ntok = filter[ii].cmd.size(); @@ -499,14 +502,20 @@ int main(int argc, char** argv) { if (best_blame) { string short_name(""); int kidno(iiofpid[argbest_blame]); + string exword = "spam"; + int excode = ex_spam; + if (filter[kidno].mode == grey) { + exword = "greylisting"; + excode = ex_grey; + } if (WIFEXITED(best_blame)) { int sts = WEXITSTATUS(best_blame); if (sts == 1) { cerr << "hi-q says: kid[" << kidno << "]" << " pid " << argbest_blame << " i.e. '" << filter[kidno].cmd[0] << "'" - << " reports spam." << endl; - panic(ex_spam); + << " reports " << exword << endl; + panic(excode); } if (sts != 0) { cerr << "hi-q says: kid " << argbest_blame -- cgit v1.2.3 From 8a896e9b2bce51742d264f6a23c4cce544ec5af7 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 18:55:57 -0700 Subject: might even be working : greylist --- tools/greylist.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index 8adac05..d769ff4 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -9,6 +9,8 @@ #include /* for ENOENT */ #include /* for ofstream() */ #include /* for creat() */ +#include /* for gettimeofday() */ + using namespace std; const int sa_good = 0; @@ -21,7 +23,7 @@ string progname; void dump(const string var){ char* str = getenv(var.c_str()); - cerr << progname + cerr << progname << "[" << mypid << "] " << var; if (str) cerr << " is set to '" << str << "'" << endl; @@ -34,12 +36,53 @@ const string dirname("/var/qmail/greylist"); // 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){ - mypid = getpid(); - progname = argv[0]; + // 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; @@ -66,8 +109,8 @@ int main(int argc, char** argv){ exit(sa_syserr); } } - - string ipname = dirname + "/" + ipbase; + + ipname = dirname + "/" + ipbase; struct stat ipstat; rslt = stat(ipname.c_str(), &ipstat); if (rslt != 0){ @@ -84,9 +127,23 @@ int main(int argc, char** argv){ perror(0); } close(fd); + update("new customer", now, now); return(bug_bait_grey); - } else { - cerr << "file exists: " << ipname << endl; } - return 0; -} +// 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); +} \ No newline at end of file -- 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(-) 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 3413e333fd7ac49461cfbc89839a2b7e94ac2d3a Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 21:09:20 -0700 Subject: nicer formatting of ages --- tools/greylist.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index 3ba502d..613c1a4 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -11,6 +11,7 @@ #include /* for ofstream() */ #include /* for creat() */ #include /* for gettimeofday() */ +#include // requires apt-get install libboost-filesystem-dev: #include @@ -67,6 +68,17 @@ public: void update(const string msg, const timeval new_mod, const timeval new_ac); }; +string time_out(const int ttt){ + int sec(ttt % 60); + int min((ttt / 60) % 60); + int hr(ttt / 3600); + stringstream foo; + foo << hr + << ":" << setw(2) << setfill('0') << min + << ":" << setw(2) << setfill('0') << sec; + return foo.str(); +} + void scan(const string p){ timeval now; gettimeofday(&now, NULL); @@ -88,12 +100,12 @@ void scan(const string p){ } 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; + cout << setw(10) << time_out(mod_age) + << " " << setw(10) << time_out(ac_age); if (mod_age < minimum_age) { cout << " young"; } else if (mod_age == ac_age) { - cout << " never used"; + cout << " unused"; } } cout << '\n'; @@ -109,8 +121,8 @@ void whatsit::update(const string msg, const timeval new_mod, const timeval new_ cerr << progname << ": " << msg << ": " << ipbase; if (hostname.length()) cerr << " " << hostname; - cerr << " mod_age: " << mod_age - << " ac_age: " << ac_age + cerr << " mod_age: " << time_out(mod_age) + << " ac_age: " << time_out(ac_age) << endl; timeval upd[2] = { // beware: access illogically comes *before* modification here: -- cgit v1.2.3 From 32994e67f359a556332ad5409063749772cbc206 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 19 Jul 2012 23:27:34 -0700 Subject: implement probation period --- tools/greylist.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index 613c1a4..1e62e93 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -23,6 +23,8 @@ const int hour(60*minute); const int day(24*hour); const int minimum_age(5*minute); +const int maximum_age(32*day); +const int probation(4*hour); const int sa_good = 0; const int bug_bait_grey = 1; // qmail_queue and spamc have similar interpretations here: @@ -73,9 +75,16 @@ string time_out(const int ttt){ int min((ttt / 60) % 60); int hr(ttt / 3600); stringstream foo; - foo << hr - << ":" << setw(2) << setfill('0') << min - << ":" << setw(2) << setfill('0') << sec; + int didsome(0); + 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(); } @@ -102,10 +111,18 @@ void scan(const string p){ int ac_age = now.tv_sec - mystat.st_atime; cout << setw(10) << time_out(mod_age) << " " << setw(10) << time_out(ac_age); - if (mod_age < minimum_age) { + 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"; } else if (mod_age == ac_age) { cout << " unused"; + } else if (mod_age > maximum_age) { + cout << " expired"; + } else { + cout << " OK"; } } cout << '\n'; @@ -209,12 +226,16 @@ int whatsit::doit(){ update("early bird", mod_orig, now); return(bug_bait_grey); } - if (ac_age < 32*day) { - update("returning customer", mod_orig, now); - return 0; + 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); } - -// here if it is too old: - update("too old, starting over", now, now); - return(bug_bait_grey); -} \ No newline at end of file + if (ac_age > maximum_age) { + update("too old, starting over", now, now); + return(bug_bait_grey); + } +// if all checks are passed, must be OK: + update("returning customer", mod_orig, now); + return 0; +} -- 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(-) 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(-) 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 4fad56112022d60688e52fa75261785b51213831 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 10:09:44 -0700 Subject: format stuff in columns --- tools/columns | 33 +++++++++++++++++++++++++++++++++ tools/greylist.c | 19 +++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100755 tools/columns diff --git a/tools/columns b/tools/columns new file mode 100755 index 0000000..dcc934f --- /dev/null +++ b/tools/columns @@ -0,0 +1,33 @@ +#! /usr/bin/perl -w + +## not very scalable; stores entire document in memory + +use strict; +use List::Util qw[min max]; + +main:{ +## leftmost column is column ZERO + my @cwid = (); # width of column + + my @store = (); + + while (my $line = <>){ + chomp $line; + my @stuff = split(' ', $line); + for (my $ii = 0; $ii < 0+@stuff; $ii++){ + my $old = $cwid[$ii] || 0; + $cwid[$ii] = max(length($stuff[$ii]), $old); + } + push @store, \@stuff; + } + foreach my $line (@store) { + my @stuff = @$line; + for (my $ii = 0; $ii < 0+@stuff; $ii++){ + if ($ii) { + print " "; + } + printf("\%-$cwid[$ii]s", $stuff[$ii]); + } + print "\n"; + } +} \ No newline at end of file diff --git a/tools/greylist.c b/tools/greylist.c index 910b40c..1745b32 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -111,7 +111,7 @@ string time_out(const int ttt){ return foo.str(); } -void scan(const string p){ +void scan(const string p, const int copies=1){ timeval now; gettimeofday(&now, NULL); using namespace boost::filesystem; @@ -119,7 +119,8 @@ void scan(const string p){ 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 + 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; @@ -177,17 +178,23 @@ int main(int _argc, char** _argv){ char** argv(_argv); const string dirname("/var/qmail/greylist"); whatsit foo(argv[0], dirname); argc--; argv++; + int scanmode(0); + int copies(1); while (argc > 0) { string arg = argv[0]; argc--; argv++; if (arg == "-scan") { - scan(dirname); - return 0; - } - else { + scanmode++; + } else if (arg == "-copy") { + copies++; + } else { cerr << "Unrecognized arg: " << arg << endl; exeunt(sa_syserr); } } + if (scanmode) { + scan(dirname, copies); + return 0; + } return foo.doit(); } -- cgit v1.2.3 From e9b59f501b23b53eb6f230e0dfc4d50bb6995d45 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 11:05:02 -0700 Subject: add some exit-code processing; require TCPREMOTEHOST --- tools/greylist.c | 39 ++++++++++++++++++++----------- tools/hi-q.c | 67 +++++++++++++++++++++++++++++++++++++++++------------- tools/hi-test.conf | 2 +- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index 1745b32..1320257 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -26,10 +26,17 @@ const int day(24*hour); const int minimum_age(15*minute); const int maximum_age(32*day); const int probation(4*hour); -const int sa_good = 0; -const int bug_bait_grey = 1; -// qmail_queue and spamc have similar interpretations here: -const int sa_syserr = 71; + +// 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(greylisting, 70) ;\ +foo(syserr, 71) ;\ +foo(comerr, 74) ; + +bar +#undef foo pid_t mypid; string progname; @@ -53,7 +60,7 @@ int prefix(const string shorter, const string longer){ } void exeunt(const int sts){ - if (sts == sa_good) exit(sts); + if (sts == ex_good) exit(sts); const char* foo = getenv("HI_Q_GROUP"); if (!foo) exit(sts); @@ -188,7 +195,7 @@ int main(int _argc, char** _argv){ copies++; } else { cerr << "Unrecognized arg: " << arg << endl; - exeunt(sa_syserr); + exeunt(ex_syserr); } } if (scanmode) { @@ -203,11 +210,17 @@ int whatsit::doit(){ char* ipvar = getenv("TCPREMOTEIP"); if (!ipvar) { cerr << progname << ": TCPREMOTEIP not set???" << endl; - exeunt(sa_syserr); + exeunt(ex_syserr); } ipbase = ipvar; char* hostvar = getenv("TCPREMOTEHOST"); - if (hostvar) hostname = hostvar; + if (!hostvar) { + cerr << progname + << ": from " << ipbase + << " ... TCPREMOTEHOST not set???" << endl; + exeunt(ex_spam); + } + hostname = hostvar; // see if our directory exists: struct stat dirstat; @@ -225,7 +238,7 @@ int whatsit::doit(){ << ": mkdir failed for '" << dirname << "' : "; perror(0); - exeunt(sa_syserr); + exeunt(ex_syserr); } } @@ -247,7 +260,7 @@ int whatsit::doit(){ } close(fd); update("new customer", now, now); - exeunt(bug_bait_grey); + exeunt(ex_greylisting); } // here if stat succeeded mod_age = now.tv_sec - ipstat.st_mtime; @@ -255,16 +268,16 @@ int whatsit::doit(){ timeval mod_orig = {ipstat.st_mtime, 0}; if (mod_age < minimum_age) { update("early bird", mod_orig, now); - exeunt(bug_bait_grey); + exeunt(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); - exeunt(bug_bait_grey); + exeunt(ex_greylisting); } if (ac_age > maximum_age) { update("too old, starting over", now, now); - exeunt(bug_bait_grey); + exeunt(ex_greylisting); } // if all checks are passed, must be OK: update("returning customer", mod_orig, now); diff --git a/tools/hi-q.c b/tools/hi-q.c index 21724a1..f195508 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -27,11 +27,42 @@ using namespace std; #include // error exit codes, mostly as stated in qmail.c -const int ex_good = 0; -const int ex_spam = 21; -const int ex_grey = 70; -const int ex_syserr = 71; -const int ex_comerr = 74; +#define bar \ +foo(good, 0) ;\ +foo(spam, 21) ;\ +foo(permerr, 31) ;\ +foo(greylisting, 70) ;\ +foo(syserr, 71) ;\ +foo(comerr, 74) ; + +#define foo(name, num) const int ex_ ## name = num +bar +#undef foo + +map codemap; + + +#define bar_sa \ +foo_sa(GOOD, 0, "ham") ;\ +foo_sa(SPAM, 1, "spam") ;\ +foo_sa(USAGE, 64, "command line usage error") ;\ +foo_sa(DATAERR, 65, "data format error") ;\ +foo_sa(NOINPUT, 66, "cannot open input") ;\ +foo_sa(NOUSER, 67, "addressee unknown") ;\ +foo_sa(NOHOST, 68, "host name unknown") ;\ +foo_sa(UNAVAILABLE, 69, "service unavailable") ;\ +foo_sa(SOFTWARE, 70, "internal software error") ;\ +foo_sa(OSERR, 71, "system error (e.g., can't fork)") ;\ +foo_sa(OSFILE, 72, "critical OS file missing") ;\ +foo_sa(CANTCREAT, 73, "can't create (user) output file") ;\ +foo_sa(IOERR, 74, "input/output error") ;\ +foo_sa(TEMPFAIL, 75, "temp failure; user is invited to retry") ;\ +foo_sa(PROTOCOL, 76, "remote error in protocol") ;\ +foo_sa(NOPERM, 77, "permission denied") ;\ +foo_sa(CONFIG, 78, "configuration error") ;\ +foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)" + + #define bufsize 16384 @@ -176,8 +207,11 @@ public: int main(int argc, char** argv) { progname = *argv; mypid = getpid(); -// dump("TCPREMOTEIP"); -// dump("TCPREMOTEHOST"); + +#define foo(name, num) codemap[num] = #name ; +bar +#undef foo + int verbose(0); int kidstatus; @@ -502,15 +536,15 @@ int main(int argc, char** argv) { if (best_blame) { string short_name(""); int kidno(iiofpid[argbest_blame]); - string exword = "spam"; - int excode = ex_spam; - if (filter[kidno].mode == grey) { - exword = "greylisting"; - excode = ex_grey; - } if (WIFEXITED(best_blame)) { + string exword = "spam"; // default, for non-modern status codes + int excode = ex_spam; // default, for non-modern status codes int sts = WEXITSTATUS(best_blame); - if (sts == 1) { + if (filter[kidno].mode == grey) { + exword = codemap[sts]; + excode = sts; + } + if (exword.length()) { cerr << "hi-q says: kid[" << kidno << "]" << " pid " << argbest_blame << " i.e. '" << filter[kidno].cmd[0] << "'" @@ -523,8 +557,9 @@ int main(int argc, char** argv) { << endl; panic(ex_syserr); } else { - // should never get here unless exit status was nonzero - cerr << "hi-q: should never happen" << endl; + // should never get here + // should be no accounting for blame if there was no blame + cerr << "hi-q: should never happen: no child to blame" << endl; panic(ex_syserr); } } else if (WIFSIGNALED(best_blame)) { diff --git a/tools/hi-test.conf b/tools/hi-test.conf index d400373..a89640d 100644 --- a/tools/hi-test.conf +++ b/tools/hi-test.conf @@ -2,5 +2,5 @@ # another comment, with blank line between black hi-test x0 -snooze 10 -black hi-test x1 -snooze 1 -exit 1 -kill +black hi-test x1 -snooze 1 -exit 2 -kill qq hi-test x2 -snooze 10 -- cgit v1.2.3 From 60fd39ff24975486da7d02cdf07abae31c525529 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 12:15:59 -0700 Subject: much smarter about exit status conventions --- tools/filters.conf | 6 +++--- tools/hi-q.c | 23 ++++++++++++++++------- tools/hi-test.conf | 4 ++-- tools/hi-test2.conf | 4 ++-- tools/hi-test3.conf | 5 +++++ tools/skrewt.c | 52 +++++++++++++++++++++++++++++++++++----------------- 6 files changed, 63 insertions(+), 31 deletions(-) create mode 100755 tools/hi-test3.conf diff --git a/tools/filters.conf b/tools/filters.conf index 641b792..dfd1180 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -1,5 +1,5 @@ # configuration file for hi-q -black /var/qmail/bin/skrewt -gray /var/qmail/bin/greylist -black /usr/local/bin/spamc -Y 0 -s 1000000 +series /var/qmail/bin/skrewt +stub /var/qmail/bin/greylist +sa /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 f195508..369935e 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -68,6 +68,7 @@ foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)" void panic(const int sts) { // FIXME: stop other children + cerr << "hi-q: panic called with " << sts << endl; exit(sts); } @@ -171,7 +172,15 @@ int xclose(int arg){ extern char** environ; -typedef enum {grey, black, qq, fail} moder; +// meanings: +// sa is a filter, using not-very-expressive exit codes: 0=ham 1=spam. +// stub is not a filter; no stdin or stdout; just looks at environment. +// series is a filter. +// qq is not a filter, just an absorber. +// +// Note that series and stub use the same exit codes as qq. +// +typedef enum {series, stub, sa, qq, fail} moder; class jobber{ public: @@ -193,9 +202,9 @@ public: void setmode(const string _mode) { if (0) {} - else if (_mode == "gray") mode = grey; - else if (_mode == "grey") mode = grey; // variant spelling - else if (_mode == "black") mode = black; + else if (_mode == "sa") mode = sa; + else if (_mode == "stub") mode = stub; + else if (_mode == "series") mode = series; else if (_mode == "qq") mode = qq; else { cerr << "jobber: bad mode: " << _mode << endl; @@ -310,7 +319,7 @@ bar //xx fprintf(stderr, "Top of loop %d loose: %d\n", ii, loose_end); int kid_end; - if (filter[ii].mode != grey){ + if (filter[ii].mode != stub){ int datapipe[2]; if (loose_end) { @@ -396,7 +405,7 @@ bar } } - if (filter[ii].mode != grey){ + if (filter[ii].mode != stub){ close(loose_end); // the reading end is none of this kid's business // except last kid: writing end @@ -540,7 +549,7 @@ bar string exword = "spam"; // default, for non-modern status codes int excode = ex_spam; // default, for non-modern status codes int sts = WEXITSTATUS(best_blame); - if (filter[kidno].mode == grey) { + if (filter[kidno].mode != sa) { exword = codemap[sts]; excode = sts; } diff --git a/tools/hi-test.conf b/tools/hi-test.conf index a89640d..aa6a1cf 100644 --- a/tools/hi-test.conf +++ b/tools/hi-test.conf @@ -1,6 +1,6 @@ # comment # another comment, with blank line between -black hi-test x0 -snooze 10 -black hi-test x1 -snooze 1 -exit 2 -kill +series hi-test x0 -snooze 10 +stub hi-test x1 -snooze 1 -exit 21 -kill qq hi-test x2 -snooze 10 diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index 3c1422c..e8e4390 100644 --- a/tools/hi-test2.conf +++ b/tools/hi-test2.conf @@ -1,3 +1,3 @@ -grey hi-test x0 -snooze 10 -gray greylist +stub hi-test x0 -snooze 10 +stub greylist qq hi-test x1 -snooze 1 -exit 3 diff --git a/tools/hi-test3.conf b/tools/hi-test3.conf new file mode 100755 index 0000000..714073a --- /dev/null +++ b/tools/hi-test3.conf @@ -0,0 +1,5 @@ +#! /bin/echo 1 maxsize) { + cerr << "skrewt rejection: bigger than " << maxsize << endl; + exeunt(ex_spam); + } for (;;) { if (cin.eof()) break; if (cin.bad()) return 1; @@ -184,18 +198,22 @@ int main(int _argc, const char** _argv){ msgsize += line.length()+1; if (msgsize > maxsize) { cerr << "skrewt rejection: bigger than " << maxsize << endl; - exeunt(sa_spam); + exeunt(ex_spam); } header += "\n" + line; } - if (header.length() == 0) { + int len = header.length(); + if (len && header[len-1] == '\r') len--; // reduced length, not counting + if (len == 0) { if (!gotdate) { cerr << "skrewt rejection: no date" << endl; - exeunt(sa_spam); // disallow mail with no date + exeunt(ex_spam); // disallow mail with no date } inheads = 0; + //cerr << "end of headers" << endl; } else { +// here if it's a header line string headword; string rest; size_t where = header.find(":"); @@ -239,18 +257,18 @@ int main(int _argc, const char** _argv){ } else if (headword == "subject") { if (rest.find("-please-bounce-this-") != string::npos) { cerr << "skrewt rejection: by request" << endl; - exeunt(sa_spam); + exeunt(ex_spam); } } } - cout << header << endl; + //cout << header.length() << " ... " << header << endl; } else { string line; if (!getline(cin, line).fail()) { msgsize += line.length()+1; if (msgsize > maxsize) { cerr << "skrewt rejection: bigger than " << maxsize << endl; - exeunt(sa_spam); + exeunt(ex_spam); } if (line == "--" + boundary) { inheads = 1; @@ -266,8 +284,8 @@ int main(int _argc, const char** _argv){ if (0) cerr << "textlines: " << textlines << endl; if (!textlines) { cerr << "skrewt rejection: no text" << endl; - exeunt(sa_spam); + exeunt(ex_spam); } cerr << "skrewt normal completion" << endl; - exit(sa_good); + exit(ex_good); } -- cgit v1.2.3 From 5d8d6c4de1940413f42e7b1c913db4b233606146 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 12:50:55 -0700 Subject: builing some features to penalize spammers, by pushing the greylisting barrier into the future --- tools/greylist.c | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index 1320257..00272d8 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -95,17 +95,20 @@ public: { gettimeofday(&now, NULL); } - int doit(); + int doit(const int penalty=0); // access comes after modification: - void update(const string msg, const timeval new_mod, const timeval new_ac); + void update(const string msg, const timeval new_mod, + const timeval new_ac, const int penalty=0); }; -string time_out(const int ttt){ +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++; @@ -142,7 +145,10 @@ void scan(const string p, const int copies=1){ int ac_age = now.tv_sec - mystat.st_atime; cout << setw(10) << time_out(mod_age) << " " << setw(10) << time_out(ac_age); - if (mod_age - ac_age < minimum_age // early bird, or completely unused + if (mod_age < 0) { + cout << " penalty"; + } + 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 << "!"; @@ -165,17 +171,23 @@ void scan(const string p, const int copies=1){ } } -void whatsit::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, const int penalty){ cerr << progname << ": " << 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; + pen_mod.tv_sec += penalty; + } timeval upd[2] = { // beware: access illogically comes *before* modification here: new_ac, - new_mod + pen_mod }; utimes(ipname.c_str(), upd); } @@ -187,12 +199,19 @@ int main(int _argc, char** _argv){ whatsit foo(argv[0], dirname); argc--; argv++; int scanmode(0); int copies(1); + int penalty(0); while (argc > 0) { string arg = argv[0]; argc--; argv++; - if (arg == "-scan") { + if (prefix(arg, "-scan")) { scanmode++; - } else if (arg == "-copy") { + } else if (prefix(arg, "-copy")) { copies++; + } else if (prefix(arg, "-penalize")) { + if (!argc){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + penalty = atoi(*argv++); argc--; } else { cerr << "Unrecognized arg: " << arg << endl; exeunt(ex_syserr); @@ -203,10 +222,10 @@ int main(int _argc, char** _argv){ return 0; } - return foo.doit(); + return foo.doit(penalty); } -int whatsit::doit(){ +int whatsit::doit(const int penalty){ char* ipvar = getenv("TCPREMOTEIP"); if (!ipvar) { cerr << progname << ": TCPREMOTEIP not set???" << endl; @@ -266,20 +285,24 @@ 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 < 0) { + update("penalty box", mod_orig, now, penalty); + exeunt(ex_spam); + } if (mod_age < minimum_age) { - update("early bird", mod_orig, now); + update("early bird", mod_orig, now, penalty); exeunt(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); + update("disprobation", now, now, penalty); exeunt(ex_greylisting); } if (ac_age > maximum_age) { - update("too old, starting over", now, now); + update("too old, starting over", now, now, penalty); exeunt(ex_greylisting); } // if all checks are passed, must be OK: - update("returning customer", mod_orig, now); + update("returning customer", mod_orig, now, penalty); return 0; } -- cgit v1.2.3 From 51f3d88572ae0f8eea40996db28ece9cdd0dae18 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 13:59:53 -0700 Subject: gaaack -- fix horrible bug --- tools/skrewt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/skrewt.c b/tools/skrewt.c index d0289a6..44e885b 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -261,7 +261,8 @@ int main(int _argc, const char** _argv){ } } } - //cout << header.length() << " ... " << header << endl; + //xxxx cout << header.length() << " ... "; + cout << header << endl; } else { string line; if (!getline(cin, line).fail()) { -- cgit v1.2.3 From eb342191804df42d294e1579a880c58dd213d66d Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 16:15:13 -0700 Subject: fix up error parsing and error logging --- tools/filters.conf | 2 + tools/greylist.c | 37 +++++--- tools/hi-q.c | 263 ++++++++++++++++++++++++++++++++++------------------ tools/hi-test2.conf | 7 +- 4 files changed, 206 insertions(+), 103 deletions(-) diff --git a/tools/filters.conf b/tools/filters.conf index dfd1180..3ef7524 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -3,3 +3,5 @@ series /var/qmail/bin/skrewt stub /var/qmail/bin/greylist sa /usr/local/bin/spamc -Y 0 -s 1000000 qq /var/qmail/bin/qmail-queue + +postspam /var/qmail/bin/greylist -penalize 86400 diff --git a/tools/greylist.c b/tools/greylist.c index 00272d8..d1ff1a4 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -51,7 +51,6 @@ void dump(const string var){ } - //////////////// // little utility to help with argument parsing: // @@ -145,10 +144,13 @@ void scan(const string p, const int copies=1){ int ac_age = now.tv_sec - mystat.st_atime; cout << setw(10) << time_out(mod_age) << " " << setw(10) << time_out(ac_age); - if (mod_age < 0) { + if (0) { + + } else if (mod_age < 0) { cout << " penalty"; - } - else if (mod_age - ac_age < minimum_age // early bird, or completely unused + } 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 << "!"; @@ -182,7 +184,7 @@ void whatsit::update(const string msg, const timeval new_mod, timeval pen_mod(new_mod); if (penalty) { pen_mod = now; - pen_mod.tv_sec += penalty; + pen_mod.tv_sec += penalty; } timeval upd[2] = { // beware: access illogically comes *before* modification here: @@ -193,6 +195,8 @@ void whatsit::update(const string msg, const timeval new_mod, } int main(int _argc, char** _argv){ + progname = *_argv; + mypid = getpid(); int argc(_argc); char** argv(_argv); const string dirname("/var/qmail/greylist"); @@ -206,7 +210,8 @@ int main(int _argc, char** _argv){ scanmode++; } else if (prefix(arg, "-copy")) { copies++; - } else if (prefix(arg, "-penalize")) { + } else if (prefix(arg, "-penalize") + || prefix(arg, "-penalty")) { if (!argc){ cerr << "Option '" << arg << "' requires an argument" << endl; exeunt(ex_syserr); @@ -228,18 +233,24 @@ int main(int _argc, char** _argv){ int whatsit::doit(const int penalty){ char* ipvar = getenv("TCPREMOTEIP"); if (!ipvar) { - cerr << progname << ": TCPREMOTEIP not set???" << endl; + cerr << progname + << "[" << mypid << "] " + << " TCPREMOTEIP not set???" << endl; + // should never happen + // although you can make it happen using a weird test-harness exeunt(ex_syserr); } ipbase = ipvar; char* hostvar = getenv("TCPREMOTEHOST"); if (!hostvar) { - cerr << progname - << ": from " << ipbase - << " ... TCPREMOTEHOST not set???" << endl; + cerr << progname + << "[" << mypid << "] " + << " from " << ipbase + << " ... TCPREMOTEHOST not set???" << endl; exeunt(ex_spam); + } else { + hostname = hostvar; } - hostname = hostvar; // see if our directory exists: struct stat dirstat; @@ -289,6 +300,10 @@ int whatsit::doit(const int penalty){ update("penalty box", mod_orig, now, penalty); exeunt(ex_spam); } + if (mod_age < ac_age){ + update("paroled spammer", now, now, penalty); + exeunt(ex_greylisting); + } if (mod_age < minimum_age) { update("early bird", mod_orig, now, penalty); exeunt(ex_greylisting); diff --git a/tools/hi-q.c b/tools/hi-q.c index 369935e..f6b57e1 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -5,7 +5,7 @@ // Hint: For testing, see also hi-test.conf which invokes ./hi-test: // ./hi-q hi-test.conf -// TODO: Panic stop should signal all children. +// TODO: Exeunt stop should signal all children. // TODO: Possibly: Wait for all kids in parallel? // That's because they might finish out of order. @@ -66,9 +66,128 @@ foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)" #define bufsize 16384 -void panic(const int sts) { +// meanings: +// sa is a filter, using not-very-expressive exit codes: 0=ham 1=spam. +// stub is not a filter; no stdin or stdout; just looks at environment. +// series is a filter. +// qq is not a filter, just an absorber. +// +// Note that series and stub use the same exit codes as qq. +// +typedef enum {series, stub, sa, qq, postspam, 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 == "sa") mode = sa; + else if (_mode == "stub") mode = stub; + else if (_mode == "series") mode = series; + else if (_mode == "qq") mode = qq; + else if (_mode == "postspam") mode = postspam; + else { + cerr << "jobber: bad mode: " << _mode << endl; + mode = fail; + } + } +}; + +// klugey global variable: +vector post; + +// We are fussy about the argument types because we want +// this to compile cleanly under g++ as well as gcc, +// and each is strict about different things, such that +// one or the other will complain unless everything is +// done just right. + +// This is the way execve really behaves: +// the characters are held constant +// and the (char*) pointers are held constant: +int Execve(char const * fn, + char const * const * argv, + char const * const * env) { +// coerce the arg types to match the unwise declaration in unistd.h : + return execve(fn, (char*const*) argv, (char*const*) env); +} + +int fork_and_wait(const jobber job){ + pid_t kidpid = fork(); + if (kidpid == -1) { + cerr << "hi-q: fork failed : "; + perror(0); + exit(ex_syserr); + } + int ntok = job.cmd.size(); + const char* prog[1+ntok]; + for (int jj = 0; jj < ntok; jj++){ + prog[jj] = job.cmd[jj].c_str(); + } + prog[ntok] = 0; + + if (!kidpid){ + /*** child code ***/ + int rslt; + rslt = Execve(prog[0], prog, environ); + fprintf(stderr, "hi-q: failed to exec '%s': ", prog[0]); + perror(0); + exit(ex_syserr); + } else { + /*** parent code ***/ + int kidstatus; + pid_t somekid; + somekid = waitpid(kidpid, &kidstatus, WUNTRACED); + if (WIFEXITED(kidstatus)) { + int sts = WEXITSTATUS(kidstatus); + if (sts != ex_good && sts != ex_spam) { + cerr << "hi-q: job " << prog[0] + << " unexpectedly returns status: " << sts + << endl; + exit(sts); + } + return 0; + } else if (WIFSIGNALED(kidstatus)) { + int sig = WTERMSIG(kidstatus); + if (sig == SIGUSR1) {/* normal, no logging required */} + else cerr << "hi-q: job " << prog[0] + << " killed by signal " << sig << endl; + return(ex_syserr); + } else { + /* paused, not dead */ + } + } + return 0; +} + +int fork_and_wait(vector post){ + for(vector::const_iterator foo = post.begin(); + foo != post.end(); foo++) { + int rslt = fork_and_wait(*foo); + if (rslt) return rslt; + } + return 0; +} + +void exeunt(const int sts) { // FIXME: stop other children - cerr << "hi-q: panic called with " << sts << endl; + //xxxx cerr << "hi-q: exeunt called with " << sts << endl; + if (sts == ex_spam) fork_and_wait(post); exit(sts); } @@ -83,7 +202,7 @@ void slurp(const int inch, const int ouch){ if (got < 0) { fprintf(stderr, "hi-q: input error: "); perror(0); - panic(ex_comerr); + exeunt(ex_comerr); } todo = got; @@ -92,7 +211,7 @@ void slurp(const int inch, const int ouch){ if (sent < 0 && errno != EINTR) { fprintf(stderr, "hi-q: output error on fd%d : ", ouch); perror(0); - panic(ex_comerr); + exeunt(ex_comerr); } todo -= sent; } @@ -126,22 +245,6 @@ void blurb(const int ii, const pid_t* kidpid) { } -// We are fussy about the argument types because we want -// this to compile cleanly under g++ as well as gcc, -// and each is strict about different things, such that -// one or the other will complain unless everything is -// done just right. - -// This is the way execve really behaves: -// the characters are held constant -// and the (char*) pointers are held constant: -int Execve(char const * fn, - char const * const * argv, - char const * const * env) { -// coerce the arg types to match the unwise declaration in unistd.h : - return execve(fn, (char*const*) argv, (char*const*) env); -} - void usage() { cerr << "Usage:\n" " hi-q filter.conf\n" @@ -172,47 +275,6 @@ int xclose(int arg){ extern char** environ; -// meanings: -// sa is a filter, using not-very-expressive exit codes: 0=ham 1=spam. -// stub is not a filter; no stdin or stdout; just looks at environment. -// series is a filter. -// qq is not a filter, just an absorber. -// -// Note that series and stub use the same exit codes as qq. -// -typedef enum {series, stub, sa, 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 == "sa") mode = sa; - else if (_mode == "stub") mode = stub; - else if (_mode == "series") mode = series; - else if (_mode == "qq") mode = qq; - else { - cerr << "jobber: bad mode: " << _mode << endl; - mode = fail; - } - } -}; - int main(int argc, char** argv) { progname = *argv; mypid = getpid(); @@ -278,7 +340,14 @@ bar job.setmode(job.cmd.front()); job.cmd.erase(job.cmd.begin()); } - if (job.cmd.size()) filter.push_back(job); + // here with a properly built job descriptor + if (job.cmd.size()) { + if (job.mode == postspam) { + post.push_back(job); + } else { + filter.push_back(job); + } + } } unsigned int nkids = filter.size(); @@ -337,7 +406,7 @@ bar if (rslt < 0) { fprintf(stderr, "hi-q: could not create datapipe: "); perror(0); - panic(ex_syserr); + exeunt(ex_syserr); } //xx fprintf(stderr, "pipe: %d %d\n", datapipe[0], datapipe[1]); @@ -451,7 +520,7 @@ bar if (kidpid[ii] < 0) { fprintf(stderr, "hi-q: failure to fork kid#%d: ", ii); perror(0); - panic(ex_syserr); + exeunt(ex_syserr); } close(kid_end); @@ -509,11 +578,20 @@ 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" << endl; - return(ex_syserr); - } else if (WIFSIGNALED(kidstatus) && WTERMSIG(kidstatus) != SIGUSR1) { - cerr << "hi-q: special kid exited early" << endl; + cerr << "hi-q: special kid exited early, status " + << WEXITSTATUS(kidstatus) + << " with " << alive << " kids still alive" + << endl; return(ex_syserr); + } else if (WIFSIGNALED(kidstatus)) { + int sig = WTERMSIG(kidstatus); + if (sig == SIGUSR1) {/* normal, no logging required */} + else { + cerr << "hi-q: special kid killed by signal " + << sig << endl; + // this is not normal + return(ex_syserr); + } } else { /* paused, not dead */ } @@ -542,42 +620,47 @@ bar /////////////////// // decode the best reason why the filter-chain terminated + //xx cerr << "cleanup: " << best_blame << endl; if (best_blame) { string short_name(""); int kidno(iiofpid[argbest_blame]); if (WIFEXITED(best_blame)) { - string exword = "spam"; // default, for non-modern status codes - int excode = ex_spam; // default, for non-modern status codes + string exword = "???"; // default, should never happen + int excode = ex_syserr; // default, should never happen int sts = WEXITSTATUS(best_blame); - if (filter[kidno].mode != sa) { - exword = codemap[sts]; - excode = sts; - } - if (exword.length()) { - cerr << "hi-q says: kid[" << kidno << "]" - << " pid " << argbest_blame - << " i.e. '" << filter[kidno].cmd[0] << "'" - << " reports " << exword << endl; - panic(excode); - } - if (sts != 0) { - cerr << "hi-q says: kid " << argbest_blame - << " exited with bad status: " << sts - << endl; - panic(ex_syserr); - } else { + if (sts == 0){ // should never get here // should be no accounting for blame if there was no blame cerr << "hi-q: should never happen: no child to blame" << endl; - panic(ex_syserr); + exeunt(ex_syserr); + } + + if (filter[kidno].mode != sa) { + exword = codemap[sts]; + excode = sts; + } else { // here to translate spamc results + if (sts == 1) { + excode = ex_spam; + exword = "spam"; + } else { + excode = ex_syserr; + stringstream foo; + foo << "bad status: " << sts; + exword = foo.str(); + } } + cerr << "hi-q concludes: kid[" << kidno << "]" + << " pid " << argbest_blame + << " i.e. '" << filter[kidno].cmd[0] << "'" + << " reports " << exword << endl; + exeunt(excode); } else if (WIFSIGNALED(best_blame)) { int sig = WTERMSIG(best_blame); cerr << "hi-q says: kid " << argbest_blame << " was killed by signal " << sig << endl; // if the *best* blame is a kill, that's not normal - panic(ex_syserr); + exeunt(ex_syserr); } } diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index e8e4390..51d0361 100644 --- a/tools/hi-test2.conf +++ b/tools/hi-test2.conf @@ -1,3 +1,6 @@ -stub hi-test x0 -snooze 10 +stub hi-test x0 -snooze 2 +stub hi-test x0 -snooze 1 -exit 0 stub greylist -qq hi-test x1 -snooze 1 -exit 3 +qq hi-test x1 -snooze 3 -exit 3 + +postspam /bin/echo post spam ! -- cgit v1.2.3 From 4e298a27d17d0d0a4181b302b4237363396eb0ed Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 20 Jul 2012 17:02:38 -0700 Subject: more regularization of reporting --- tools/filters.conf | 2 +- tools/greylist.c | 41 ++++++++++++++++++++++++++++------------- tools/hi-q.c | 41 ++++++++++++++++++++++++++++------------- tools/hi-test2.conf | 2 +- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/tools/filters.conf b/tools/filters.conf index 3ef7524..9342441 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -4,4 +4,4 @@ stub /var/qmail/bin/greylist sa /usr/local/bin/spamc -Y 0 -s 1000000 qq /var/qmail/bin/qmail-queue -postspam /var/qmail/bin/greylist -penalize 86400 +postspam /var/qmail/bin/greylist -suffix (post) -penalize 86400 diff --git a/tools/greylist.c b/tools/greylist.c index d1ff1a4..465a78e 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -88,6 +88,8 @@ public: string hostname; int mod_age; int ac_age; + string suffix; + string progid; whatsit(const string name, const string _dirname) : dirname(_dirname), progname(name), mypid(getpid()), mod_age(0), ac_age(0) @@ -98,8 +100,16 @@ public: // access comes after modification: void update(const string msg, const timeval new_mod, const timeval new_ac, const int penalty=0); + void bind(); }; +void whatsit::bind(){ + stringstream foo; + foo << progname << suffix + << "[" << mypid << "]"; + progid = foo.str(); +} + string time_out(const int _ttt){ int ttt(abs(_ttt)); int sec(ttt % 60); @@ -120,7 +130,7 @@ string time_out(const int _ttt){ return foo.str(); } -void scan(const string p, const int copies=1){ +void scan(const string progid, const string p, const int copies=1){ timeval now; gettimeofday(&now, NULL); using namespace boost::filesystem; @@ -136,7 +146,7 @@ void scan(const string p, const int copies=1){ string fn = p + "/" + basename; int rslt = stat(fn.c_str(), &mystat); if (rslt != 0){ - cerr << progname << ": stat failed for '" + cerr << progid << ": stat failed for '" << fn << "' : "; perror(0); } @@ -175,7 +185,7 @@ void scan(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 << progname << ": " + cerr << progid << ": " << msg << ": " << ipbase; if (hostname.length()) cerr << " " << hostname; cerr << " mod_age: " << time_out(mod_age) @@ -195,7 +205,6 @@ void whatsit::update(const string msg, const timeval new_mod, } int main(int _argc, char** _argv){ - progname = *_argv; mypid = getpid(); int argc(_argc); char** argv(_argv); @@ -217,13 +226,21 @@ int main(int _argc, char** _argv){ exeunt(ex_syserr); } penalty = 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); } } + foo.bind(); + if (scanmode) { - scan(dirname, copies); + scan(foo.progid, dirname, copies); return 0; } @@ -233,8 +250,7 @@ int main(int _argc, char** _argv){ int whatsit::doit(const int penalty){ char* ipvar = getenv("TCPREMOTEIP"); if (!ipvar) { - cerr << progname - << "[" << mypid << "] " + cerr << progid << " TCPREMOTEIP not set???" << endl; // should never happen // although you can make it happen using a weird test-harness @@ -243,8 +259,7 @@ int whatsit::doit(const int penalty){ ipbase = ipvar; char* hostvar = getenv("TCPREMOTEHOST"); if (!hostvar) { - cerr << progname - << "[" << mypid << "] " + cerr << progid << " from " << ipbase << " ... TCPREMOTEHOST not set???" << endl; exeunt(ex_spam); @@ -257,13 +272,13 @@ int whatsit::doit(const int penalty){ int rslt = stat(dirname.c_str(), &dirstat); if (rslt != 0){ if (errno != ENOENT) { - cerr << progname << ": stat failed for '" + cerr << progid << ": stat failed for '" << dirname << "' : "; perror(0); } rslt = mkdir(dirname.c_str(), 0755); if (rslt != 0) { - cerr << progname + cerr << progid << "uid " << getuid() << ": mkdir failed for '" << dirname << "' : "; @@ -277,14 +292,14 @@ int whatsit::doit(const int penalty){ rslt = stat(ipname.c_str(), &ipstat); if (rslt != 0){ if (errno != ENOENT) { - cerr << progname << ": stat failed for '" + cerr << progid << ": stat failed for '" << ipname << "' : "; perror(0); } ofstream foo; int fd = creat(ipname.c_str(), 0644); if (fd < 0){ - cerr << progname << ": create failed for '" + cerr << progid << ": create failed for '" << ipname << "' : "; perror(0); } diff --git a/tools/hi-q.c b/tools/hi-q.c index f6b57e1..26e67a7 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -258,12 +258,11 @@ void usage() { string progname; pid_t mypid; +string progid; void dump(const string var){ char* str = getenv(var.c_str()); - cerr << progname - << "[" << mypid << "] " - << var; + cerr << progid << var; if (str) cerr << " is set to '" << str << "'" << endl; else cerr << " is not set." << endl; } @@ -276,8 +275,16 @@ int xclose(int arg){ extern char** environ; int main(int argc, char** argv) { - progname = *argv; - mypid = getpid(); + { + 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 << "]"; + progid = binder.str(); + } #define foo(name, num) codemap[num] = #name ; bar @@ -649,14 +656,18 @@ bar exword = foo.str(); } } - cerr << "hi-q concludes: kid[" << kidno << "]" - << " pid " << argbest_blame - << " i.e. '" << filter[kidno].cmd[0] << "'" + cerr << progid + << " concludes: kid[" << kidno << "]" + << " i.e. " << filter[kidno].cmd[0] + << "[" << argbest_blame << "]" << " reports " << exword << endl; exeunt(excode); } else if (WIFSIGNALED(best_blame)) { int sig = WTERMSIG(best_blame); - cerr << "hi-q says: kid " << argbest_blame + cerr << progid + << " concludes: kid[" << kidno << "]" + << " i.e. " << filter[kidno].cmd[0] + << "[" << argbest_blame << "]" << " was killed by signal " << sig << endl; // if the *best* blame is a kill, that's not normal @@ -677,14 +688,18 @@ bar waitpid(special_pid, &kidstatus, WUNTRACED); if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); - cerr << "hi-q says: qq program " << kidpid[nkids-1] - << " i.e. '" << filter[nkids-1].cmd[0] << "'" + cerr << progid + << " says: qq program" + << " i.e. " << filter[nkids-1].cmd[0] + << "[" << kidpid[nkids-1] << "]" << " returned status " << sts << endl; return sts; } else if (WIFSIGNALED(kidstatus)) { - cerr << "hi-q says: qq program " << kidpid[nkids-1] - << " i.e. '" << filter[nkids-1].cmd[0] << "'" + cerr << progid + << " says: qq program" + << " i.e. " << filter[nkids-1].cmd[0] + << "[" << kidpid[nkids-1] << "]" << " was killed by signal " << WTERMSIG(kidstatus) << endl; return ex_syserr; diff --git a/tools/hi-test2.conf b/tools/hi-test2.conf index 51d0361..90997b4 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 +stub greylist -suffix (sfx) qq hi-test x1 -snooze 3 -exit 3 postspam /bin/echo post spam ! -- cgit v1.2.3 From 60ebe6c00a2868e6bb69ef30cf04e5568276808b Mon Sep 17 00:00:00 2001 From: John Denker 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(-) 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 From d747e6cea96000540619a1f1e2a33a4f01a25a67 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 06:26:42 -0700 Subject: don't return messy exit status codes in non-check mode --- tools/greylist.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index c5c891f..d76ba08 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -264,19 +264,20 @@ int main(int _argc, char** _argv){ } int sts = foo.doit(penalty); + if (sts == ex_syserr) return sts; + if (!check) return ex_good; -// perform some extra checks. +// 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 - if (check){ - char* hostvar = getenv("TCPREMOTEHOST"); - if (!hostvar) { - cerr << foo.progid - << " from " << foo.ipbase - << " ... TCPREMOTEHOST not set???" << endl; - exeunt(ex_spam); - } + + char* hostvar = getenv("TCPREMOTEHOST"); + if (!hostvar) { + cerr << foo.progid + << " from " << foo.ipbase + << " ... TCPREMOTEHOST not set???" << endl; + exeunt(ex_spam); } exeunt(sts); } -- cgit v1.2.3 From acca30bef01fb07b34b0220fa96bcc7cf0df52f9 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 09:47:14 -0700 Subject: adding some code to check DNS consistency --- tools/greylist.c | 113 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index d76ba08..b64636e 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -3,7 +3,7 @@ #include #include -#include /* for stat() */ +#include /* for stat(), getaddrinfo() */ #include /* for stat() */ #include /* for stat() */ #include /* for perror */ @@ -17,6 +17,10 @@ // requires apt-get install libboost-filesystem-dev: #include +#include /* for getaddrinfo() */ +#include /* for getaddrinfo() */ +#include /* for memset() */ + using namespace std; const int minute(60); @@ -84,6 +88,8 @@ public: string progname; pid_t mypid; timeval now; + char* ipvar; + char* hostvar; string ipbase; string ipname; string hostname; @@ -104,7 +110,8 @@ public: // access comes after modification: void update(const string msg, const timeval new_mod, const timeval new_ac, const int penalty=0); - void bind(); + int setup(); + int check_dns(); }; string basename(const string path){ @@ -113,11 +120,24 @@ string basename(const string path){ return path; } -void whatsit::bind(){ +int whatsit::setup(){ stringstream foo; - foo << basename(progname) << suffix + foo << basename(progname) << suffix << "[" << mypid << "]"; progid = foo.str(); + + ipvar = getenv("TCPREMOTEIP"); + 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); + } + ipbase = ipvar; + hostvar = getenv("TCPREMOTEHOST"); + if (hostvar) hostname = hostvar; + return 0; } string time_out(const int _ttt){ @@ -218,6 +238,7 @@ void whatsit::update(const string msg, const timeval new_mod, utimes(ipname.c_str(), upd); } + int main(int _argc, char** _argv){ mypid = getpid(); int argc(_argc); @@ -256,7 +277,7 @@ int main(int _argc, char** _argv){ exeunt(ex_syserr); } } - foo.bind(); + if (foo.setup()) return ex_syserr; if (scanmode) { scan(foo.progid, dirname, copies); @@ -269,31 +290,15 @@ int main(int _argc, char** _argv){ // 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 +// (a) make more thorough DNS checks, and +// (b) move all the DNS checking to a separate module - char* hostvar = getenv("TCPREMOTEHOST"); - if (!hostvar) { - cerr << foo.progid - << " from " << foo.ipbase - << " ... TCPREMOTEHOST not set???" << endl; - exeunt(ex_spam); - } + int dns = foo.check_dns(); + if (dns == ex_syserr) return dns; exeunt(sts); } int whatsit::doit(const int penalty){ - char* ipvar = getenv("TCPREMOTEIP"); - 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); - } - ipbase = ipvar; - char* hostvar = getenv("TCPREMOTEHOST"); - if (hostvar) hostname = hostvar; // see if our directory exists: struct stat dirstat; @@ -365,3 +370,61 @@ int whatsit::doit(const int penalty){ update("returning customer", mod_orig, now, penalty); return 0; } + +int whatsit::check_dns(){ + char* hostvar = getenv("TCPREMOTEHOST"); + if (!hostvar) { + cerr << progid + << " from " << ipbase + << " ... TCPREMOTEHOST not set???" << endl; + exeunt(ex_spam); + } + + struct addrinfo *result; + struct addrinfo *ipresult; + struct addrinfo *res; + addrinfo hints; + int error; + + /* resolve the domain name into a list of addresses */ + 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(hostvar, NULL, &hints, &result); + if (error) { + cerr << "error in getaddrinfo for " << hostvar + << " : " << gai_strerror(error) << endl; + return ex_syserr; + } + + error = getaddrinfo(ipvar, NULL, &hints, &ipresult); + if (error) { + cerr << "error 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){ + void *numericAddress; + + sa_family_t fam = ((sockaddr *)res->ai_addr)->sa_family; + int addrsize; + switch (fam) { + case AF_INET: + numericAddress = &(((sockaddr_in *)res->ai_addr)->sin_addr.s_addr); + addrsize = sizeof(in_addr); + break; + case AF_INET6: + numericAddress = &(((sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr); + addrsize = sizeof(in6_addr); + break; + default: + cerr << "?Unknown address family " << fam << endl; + return(ex_syserr); + } + } + return 0; +} -- cgit v1.2.3 From effe1d81c837e1b887c307ed607459791bd7c8b5 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sat, 21 Jul 2012 10:45:40 -0700 Subject: doesn't work, don't know why --- tools/greylist.c | 81 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/tools/greylist.c b/tools/greylist.c index b64636e..f9a4ebc 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -371,6 +371,45 @@ int whatsit::doit(const int penalty){ return 0; } +typedef vector VU; + +class VUx : public VU { +public: + sa_family_t fam; +}; + +VUx parse_sockaddr(const sockaddr* ai_addr) { + void* numericAddress; + VUx rslt; + int addrsize; + 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; + (VU)rslt = VU(foo, foo+addrsize); + cerr << "asdf " << rslt.size() << " ... " << VU(foo, foo+addrsize).size() << endl; + 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(){ char* hostvar = getenv("TCPREMOTEHOST"); if (!hostvar) { @@ -392,39 +431,41 @@ int whatsit::check_dns(){ // restrict to TCP only; otherwise we get N records per address hints.ai_protocol = IPPROTO_TCP; #endif - error = getaddrinfo(hostvar, NULL, &hints, &result); + + error = getaddrinfo(ipvar, NULL, &hints, &ipresult); if (error) { - cerr << "error in getaddrinfo for " << hostvar + cerr << "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; + } - error = getaddrinfo(ipvar, NULL, &hints, &ipresult); + VU ipAddr = parse_sockaddr(ipresult->ai_addr); + error = getaddrinfo(hostvar, NULL, &hints, &result); if (error) { - cerr << "error in getaddrinfo for " << ipvar + cerr << "error in getaddrinfo for " << hostvar << " : " << gai_strerror(error) << endl; return ex_syserr; } // loop over all returned results and check for a match. + vector checked_hosts; for (res = result; res != NULL; res = res->ai_next){ - void *numericAddress; - - sa_family_t fam = ((sockaddr *)res->ai_addr)->sa_family; - int addrsize; - switch (fam) { - case AF_INET: - numericAddress = &(((sockaddr_in *)res->ai_addr)->sin_addr.s_addr); - addrsize = sizeof(in_addr); - break; - case AF_INET6: - numericAddress = &(((sockaddr_in6 *)res->ai_addr)->sin6_addr.s6_addr); - addrsize = sizeof(in6_addr); - break; - default: - cerr << "?Unknown address family " << fam << endl; - return(ex_syserr); + 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; + goto done; } } + cerr << "no match" << endl; +done: return 0; } -- 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(-) 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(-) 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(-) 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 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 6c56d656bd407bc5fc4dd713aacb553ebefee892 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 22 Jul 2012 14:44:49 -0700 Subject: regularize log/progress messages --- tools/filters.conf | 3 +- tools/greylist.c | 93 ++++++++++++++++++++++++++++++++---------------------- tools/skrewt.c | 33 ++++++++++++++----- 3 files changed, 83 insertions(+), 46 deletions(-) diff --git a/tools/filters.conf b/tools/filters.conf index f5b35f5..7768e6a 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -4,4 +4,5 @@ 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 -v +# postspam /var/qmail/bin/greylist -suffix (post) -penalize 86400 -v +postspam /var/qmail/bin/greylist -suffix (post) -penalize 1 -v diff --git a/tools/greylist.c b/tools/greylist.c index 063c3d0..fd5ac4f 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -38,6 +38,7 @@ const int probation(4*hour); #define bar foo(good, 0) ;\ foo(spam, 21) ;\ foo(penaltybox, 22) ;\ +foo(badDNS, 23) ;\ foo(greylisting, 70) ;\ foo(syserr, 71) ;\ foo(comerr, 74) ; @@ -118,6 +119,7 @@ public: const timeval new_ac, const int penalty=0); int setup(); int check_dns(); + int check_dns_sub(string &addr, string &host, vector &checked); }; string basename(const string path){ @@ -133,14 +135,7 @@ int whatsit::setup(){ progid = foo.str(); ipvar = getenv("TCPREMOTEIP"); - 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); - } - ipbase = ipvar; + if (ipvar) ipbase = ipvar; hostvar = getenv("TCPREMOTEHOST"); if (hostvar) hostname = hostvar; return 0; @@ -306,6 +301,14 @@ int main(int _argc, char** _argv){ int whatsit::doit(const int penalty){ + 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); @@ -356,6 +359,7 @@ int whatsit::doit(const int penalty){ 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); return(ex_greylisting); } @@ -427,23 +431,35 @@ int diff(const VU aaa, const VU bbb){ } int whatsit::check_dns(){ - char* hostvar = getenv("TCPREMOTEHOST"); - if (!hostvar) { - cerr << progid - << " from " << ipbase - << " ... TCPREMOTEHOST not set???" << endl; - exeunt(ex_spam); - } + 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; - 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)); #if 1 // restrict to TCP only; otherwise we get N records per address @@ -451,43 +467,46 @@ int whatsit::check_dns(){ #endif error = getaddrinfo(ipvar, NULL, &hints, &ipresult); - if (error) { - cerr << "error in getaddrinfo for " << ipvar + 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_dnserr; + return ex_syserr; } if (!ipresult) { cerr << "should never happen (addr with no addrs?)" << endl; - return ex_dnserr; + 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 << "error in getaddrinfo for " << hostvar - << " : " << gai_strerror(error) << endl; - return ex_dnserr; + 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. - vector checked_hosts; 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; } } - 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; - return ex_dnserr; + return ex_badDNS; done: return 0; diff --git a/tools/skrewt.c b/tools/skrewt.c index 44e885b..6de3dd9 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -11,6 +11,7 @@ #include #include /* perror */ +#include using namespace std; @@ -134,6 +135,15 @@ void exeunt(const int sts){ exit(sts); } +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; + //////////////////////////////////////////////////////////// int main(int _argc, const char** _argv){ //// pid_t pid = getpid(); @@ -141,7 +151,14 @@ int main(int _argc, const char** _argv){ //// cout << getpgid(pid) << endl; int argc(_argc); const char **argv(_argv); - string progname(*argv); argv++; argc--; + { + progname = *argv++; argc--; + mypid = getpid(); + stringstream binder; + binder << basename(progname) << "[" << mypid << "]"; + progid = binder.str(); + } + int maxsize(1000000); @@ -183,7 +200,7 @@ int main(int _argc, const char** _argv){ if (getline(cin, header).fail()) continue; msgsize += header.length()+1; if (msgsize > maxsize) { - cerr << "skrewt rejection: bigger than " << maxsize << endl; + cerr << progid << " rejection: bigger than " << maxsize << endl; exeunt(ex_spam); } for (;;) { @@ -197,7 +214,7 @@ int main(int _argc, const char** _argv){ if (getline(cin, line).fail()) continue; msgsize += line.length()+1; if (msgsize > maxsize) { - cerr << "skrewt rejection: bigger than " << maxsize << endl; + cerr << progid << " rejection: bigger than " << maxsize << endl; exeunt(ex_spam); } header += "\n" + line; @@ -206,7 +223,7 @@ int main(int _argc, const char** _argv){ if (len && header[len-1] == '\r') len--; // reduced length, not counting if (len == 0) { if (!gotdate) { - cerr << "skrewt rejection: no date" << endl; + cerr << progid << " rejection: no date" << endl; exeunt(ex_spam); // disallow mail with no date } inheads = 0; @@ -256,7 +273,7 @@ int main(int _argc, const char** _argv){ gotdate++; } else if (headword == "subject") { if (rest.find("-please-bounce-this-") != string::npos) { - cerr << "skrewt rejection: by request" << endl; + cerr << progid << " rejection: by request" << endl; exeunt(ex_spam); } } @@ -268,7 +285,7 @@ int main(int _argc, const char** _argv){ if (!getline(cin, line).fail()) { msgsize += line.length()+1; if (msgsize > maxsize) { - cerr << "skrewt rejection: bigger than " << maxsize << endl; + cerr << progid << " rejection: bigger than " << maxsize << endl; exeunt(ex_spam); } if (line == "--" + boundary) { @@ -284,9 +301,9 @@ int main(int _argc, const char** _argv){ } if (0) cerr << "textlines: " << textlines << endl; if (!textlines) { - cerr << "skrewt rejection: no text" << endl; + cerr << progid << " rejection: no text" << endl; exeunt(ex_spam); } - cerr << "skrewt normal completion" << endl; + cerr << progid << " normal completion" << endl; exit(ex_good); } -- cgit v1.2.3 From 065d189b49449c073cfa23cba4172060abe36c6c Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 22 Jul 2012 19:10:38 -0700 Subject: implement "-stain" feature; the "-penalty" feature was a baaaad idea --- tools/filters.conf | 2 +- tools/greylist.c | 57 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/tools/filters.conf b/tools/filters.conf index 7768e6a..bd8eb33 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -5,4 +5,4 @@ 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 -v -postspam /var/qmail/bin/greylist -suffix (post) -penalize 1 -v +postspam /var/qmail/bin/greylist -suffix (post) -stain 1 -v diff --git a/tools/greylist.c b/tools/greylist.c index fd5ac4f..92e638f 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -113,10 +113,10 @@ public: { gettimeofday(&now, NULL); } - int doit(const int penalty=0); + int doit(const int penalty, const int stain); // access comes after modification: void update(const string msg, const timeval new_mod, - const timeval new_ac, const int penalty=0); + const timeval new_ac, const int penalty, const int stain); int setup(); int check_dns(); int check_dns_sub(string &addr, string &host, vector &checked); @@ -216,27 +216,37 @@ 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){ + const timeval new_ac, const int penalty, const int stain){ 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; + 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: - new_ac, + stain_ac, pen_mod }; - utimes(ipname.c_str(), upd); + if (utimes(ipname.c_str(), upd)) + cerr << "oops" << endl; } @@ -249,6 +259,7 @@ int main(int _argc, char** _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++; @@ -267,6 +278,12 @@ int main(int _argc, char** _argv){ 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; @@ -285,7 +302,7 @@ int main(int _argc, char** _argv){ return 0; } - int sts = foo.doit(penalty); + int sts = foo.doit(penalty, stain); if (sts == ex_syserr) return sts; if (!check) return ex_good; @@ -299,7 +316,7 @@ int main(int _argc, char** _argv){ exeunt(sts); } -int whatsit::doit(const int penalty){ +int whatsit::doit(const int penalty, const int stain){ if (!ipvar) { cerr << progid @@ -346,7 +363,7 @@ int whatsit::doit(const int penalty){ perror(0); } close(fd); - update("new customer", now, now, penalty); + update("new customer", now, now, penalty, stain); return(ex_greylisting); } @@ -355,29 +372,29 @@ int whatsit::doit(const int penalty){ 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); + 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); + update("paroled spammer", now, now, penalty, stain); return(ex_greylisting); } if (mod_age < minimum_age) { - update("early bird", mod_orig, now, penalty); + 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); + update("disprobation", now, now, penalty, stain); return(ex_greylisting); } if (ac_age > maximum_age) { - update("too old, starting over", now, now, penalty); + 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); + update("returning customer", mod_orig, now, penalty, stain); return 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 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: Sun, 22 Jul 2012 20:27:21 -0700 Subject: progress toward cleaning up logic of various modes and how the use their pipes --- tools/hi-q.c | 32 +++++++++++++++++++++++++------- tools/hi-test.c | 20 ++++++++++++++++++++ tools/hi-test4.conf | 10 ++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100755 tools/hi-test4.conf diff --git a/tools/hi-q.c b/tools/hi-q.c index 8766b08..39c68e3 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -425,13 +425,31 @@ bar // For N-1 kids, the loose end feeds forward. // It will be written by this kid and read by the next kid. - // For the last kid, the loose end connects to hi-q. - // It will be written by hi-q and read by the last kid. - - int lastkid = (ii == nkids-1); - #define flip(a,b) (lastkid ? b : a) - loose_end = datapipe[flip(rEnd, wEnd)]; - kid_end = datapipe[flip(wEnd, rEnd)]; + // For the special kid, the loose end will be its nonstandard input. + // It will be written by us (hi-q) and read by the last kid. + + switch (filter[ii].mode) { + case series: + case sa: + loose_end = datapipe[rEnd]; + kid_end = datapipe[wEnd]; + break; + case qq: + loose_end = datapipe[wEnd]; // reverse of normal "series" case + kid_end = datapipe[rEnd]; // reverse of normal "series" case + break; + case postspam: + case stub: // didn't need a connection at all + cerr << "ignoring fd " << datapipe[wEnd] + << " and " << datapipe[rEnd] + << endl; + xclose(datapipe[wEnd]); + xclose(datapipe[rEnd]); + break; + case fail: + cerr << "should never happen: invalid filter" << endl; + exeunt(ex_syserr); + } } kidpid[ii] = fork(); diff --git a/tools/hi-test.c b/tools/hi-test.c index 47128a9..e2626cc 100644 --- a/tools/hi-test.c +++ b/tools/hi-test.c @@ -50,10 +50,22 @@ void dump(const string var){ else cerr << " is not set." << endl; } +void countsome(const int unit){ + char buf[3000]; + int total(0); + for (;;) { + int rslt = read(unit, buf, sizeof(buf)); + if (rslt <= 0) break; + total += rslt; + } + cerr << "read " << total << " bytes from unit " << unit << endl; +} + int main(int _argc, const char** _argv){ int snooze(0); int status(0); int killmode(0); + int countmode(0); int argc(_argc); const char **argv(_argv); progname = *argv; argv++; argc--; @@ -84,6 +96,10 @@ int main(int _argc, const char** _argv){ killmode++; continue; } + if (prefix(arg, "-count")) { + countmode++; + continue; + } if (arg.substr(0,1) == "x") { continue; } @@ -103,6 +119,10 @@ int main(int _argc, const char** _argv){ if (foo) cerr << " HI_Q_GROUP: " << foo; cerr << endl; sleep(snooze); + if (countmode) { + countsome(0); + countsome(1); + } if (killmode) exeunt(status); exit(status); } diff --git a/tools/hi-test4.conf b/tools/hi-test4.conf new file mode 100755 index 0000000..caabbd2 --- /dev/null +++ b/tools/hi-test4.conf @@ -0,0 +1,10 @@ +#!/usr/local/bin/bash-c set -x ; /bin/echo "a b c" | 1 Date: Sun, 22 Jul 2012 21:19:50 -0700 Subject: change from if-statements to switch-statements, to make sure all cases get handled --- tools/hi-q.c | 158 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 67 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 39c68e3..114570f 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -299,7 +299,7 @@ bar int kidstatus; int rslt; - int loose_end = 0; + int loose_end = 0; // our original stdin typedef vector VS; vector filter; @@ -368,7 +368,7 @@ bar // This makes it safe to assume that nkids-1 is non-negative. if (nkids == 0) exit(0); // nothing to do - if (0 && verbose) for (unsigned int ii = 0; ii < nkids; ii++) { + if (verbose) for (unsigned int ii = 0; ii < nkids; ii++) { cerr << "hi-q filter[" << ii << "] :; "; for (VS::const_iterator token = filter[ii].cmd.begin(); token != filter[ii].cmd.end(); token++){ @@ -397,59 +397,66 @@ bar map iiofpid; for (unsigned int ii=0; ii < nkids; ii++){ /* loop starting all kids */ -//xx fprintf(stderr, "Top of loop %d loose: %d\n", ii, loose_end); - + //xx cerr << "top of loop ... loose end " << loose_end << " for " << ii << endl; + if (loose_end > 20) exit(99); int kid_end; - if (filter[ii].mode != stub){ - int datapipe[2]; - - if (loose_end) { - close(0); - dup2(loose_end, 0); - close(loose_end); - } - - // Create a pipe, which will be used to connect - // this child's fd1 to the next child's fd0 ... - // except for the last kid, which reads both fd0 and fd1, - // while writing nothing. - rslt = pipe(datapipe); - if (rslt < 0) { - fprintf(stderr, "hi-q: could not create datapipe: "); - perror(0); - exeunt(ex_syserr); - } - - //xx fprintf(stderr, "pipe: %d %d\n", datapipe[0], datapipe[1]); + int datapipe[2]; + + switch (filter[ii].mode) { + case series: + case qq: + case sa: +// connect *old* loose end to this kid's stdin + //xx cerr << "moving old loose end " << loose_end << " to 0 for " << ii << endl; + if (loose_end) { + close(0); + dup2(loose_end, 0); + close(loose_end); + } + +// Create a pipe, which will be used to connect +// this child's fd1 to the next child's fd0 ... +// except for the last kid, which reads both fd0 and fd1, +// while writing nothing. + rslt = pipe(datapipe); + if (rslt < 0) { + fprintf(stderr, "hi-q: could not create datapipe: "); + perror(0); + exeunt(ex_syserr); + } + break; + case postspam: + case stub: + // do not need to create a pipe + break; + case fail: + cerr << "should never happen: invalid filter" << endl; + exeunt(ex_syserr); + } - // For N-1 kids, the loose end feeds forward. - // It will be written by this kid and read by the next kid. - // For the special kid, the loose end will be its nonstandard input. - // It will be written by us (hi-q) and read by the last kid. +// For N-1 kids, the loose end feeds forward. +// It will be written by this kid and read by the next kid. +// For the special kid, the loose end will be its nonstandard input. +// It will be written by us (hi-q) and read by the last kid. - switch (filter[ii].mode) { - case series: - case sa: - loose_end = datapipe[rEnd]; - kid_end = datapipe[wEnd]; - break; - case qq: - loose_end = datapipe[wEnd]; // reverse of normal "series" case - kid_end = datapipe[rEnd]; // reverse of normal "series" case - break; - case postspam: - case stub: // didn't need a connection at all - cerr << "ignoring fd " << datapipe[wEnd] - << " and " << datapipe[rEnd] - << endl; - xclose(datapipe[wEnd]); - xclose(datapipe[rEnd]); - break; - case fail: - cerr << "should never happen: invalid filter" << endl; - exeunt(ex_syserr); - } + switch (filter[ii].mode) { + case series: + case sa: + loose_end = datapipe[rEnd]; + kid_end = datapipe[wEnd]; + break; + case qq: + loose_end = datapipe[wEnd]; // reverse of normal "series" case + kid_end = datapipe[rEnd]; // reverse of normal "series" case + break; + case postspam: + case stub: + // no pipe even got created. + break; + case fail: + cerr << "should never happen:: invalid filter" << endl; + exeunt(ex_syserr); } kidpid[ii] = fork(); @@ -460,6 +467,7 @@ bar } iiofpid[kidpid[ii]] = ii; if (!kidpid[ii]) { /*** child code ***/ + if (verbose) cerr << "top of kid ... loose end " << loose_end << " for " << ii << endl; pid_t kidgroup(0); // process group for all kids is // equal to pid of kid#0 @@ -488,6 +496,7 @@ bar #endif close(resync[wEnd]); // send resync + //xx cerr << "after sending resync " << ii << endl; // ... now we must wait for everybody else, because ... // ... if we do the exec(), the new process group becomes invalid ... @@ -504,23 +513,38 @@ bar } } - if (filter[ii].mode != stub){ - close(loose_end); // the reading end is none of this kid's business - // except last kid: writing end - - // Note this does an implicit close on the previously-open fd1: - rslt = dup2(kid_end, 1); // the writing end is stdout for this kid - // except last kid: nonstandard input - if (rslt < 0) { - fprintf(stderr, "hi-q: kid %d: dup2(%d,1) failed: ", ii, kid_end); - perror(0); - exit(ex_syserr); - } - - close(kid_end); // use fd1 instead now - // OK, at this point this kid is set up to read fd0 and write fd1 - // (except last kid reads fd1 as well as fd0). + if (0) cerr << "before closing loose end " << loose_end + << " and kid end " << kid_end + << " for " << ii << endl; + switch (filter[ii].mode){ + case sa: + case qq: + case series: + close(loose_end); // the reading end is none of this kid's business + // except last kid: writing end + + // Note this does an implicit close on the previously-open fd1: + rslt = dup2(kid_end, 1); // the writing end is stdout for this kid + // except last kid: nonstandard input + if (rslt < 0) { + fprintf(stderr, "hi-q: kid %d: dup2(%d,1) failed: ", ii, kid_end); + perror(0); + exit(ex_syserr); + } + close(kid_end); // use fd1 instead now + // OK, at this point this kid is set up to read fd0 and write fd1 + // (except last kid reads fd1 as well as fd0). + break; + case stub: + case postspam: + // nothing to do + break; + case fail: + cerr << "should never happen: invalid filter" << endl; + exeunt(ex_syserr); + break; } + //// probe_fd(); int ntok = filter[ii].cmd.size(); -- cgit v1.2.3 From 5543dfa42516a12403aaea93816bbd98fdf45318 Mon Sep 17 00:00:00 2001 From: John Denker 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(-) 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 c17ca9673ddb63794e42edb78989a76385c68ce8 Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 23 Jul 2012 08:44:21 -0700 Subject: shorten the #! shebang line --- tools/hi-test4.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/hi-test4.conf b/tools/hi-test4.conf index caabbd2..850784e 100755 --- a/tools/hi-test4.conf +++ b/tools/hi-test4.conf @@ -1,9 +1,10 @@ -#!/usr/local/bin/bash-c set -x ; /bin/echo "a b c" | 1 Date: Mon, 23 Jul 2012 08:44:42 -0700 Subject: fix very small bug --- tools/greylist.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/greylist.c b/tools/greylist.c index 92e638f..89396e7 100644 --- a/tools/greylist.c +++ b/tools/greylist.c @@ -73,6 +73,10 @@ void exeunt(const int sts){ if (sts == ex_penaltybox) exit(sts); #endif +#ifndef KILL_GROUP + exit(sts); +#endif + const char* foo = getenv("HI_Q_GROUP"); if (!foo) exit(sts); @@ -218,7 +222,7 @@ 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, const int stain){ if (verbosity){ - cerr << progid << ": "; + if (penalty || stain || verbosity>1) cerr << progid << ": "; if (penalty) cerr << " penalty " << penalty; if (stain) cerr << " stain " << stain; if (verbosity > 1) { -- cgit v1.2.3 From 1c7804ee064dadadb06e786efcf4992e2a3b2028 Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 23 Jul 2012 12:42:23 -0700 Subject: much more logical about keeping track of pipes and how they are used --- .gitignore | 1 + tools/hi-q.c | 195 ++++++++++++++++++++++++++++++++-------------------- tools/hi-test.c | 33 +++++++-- tools/hi-test.conf | 7 +- tools/hi-test5.conf | 6 ++ 5 files changed, 161 insertions(+), 81 deletions(-) create mode 100755 tools/hi-test5.conf diff --git a/.gitignore b/.gitignore index 574561f..ad1d359 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *~ *.a *.o +*.d *.lib *.0 *.orig diff --git a/tools/hi-q.c b/tools/hi-q.c index 114570f..5ee7688 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -64,6 +64,7 @@ foo_sa(CONFIG, 78, "configuration error") ;\ foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)" +typedef enum {MSG, ENV} channeler; #define bufsize 16384 @@ -198,6 +199,7 @@ void slurp(const int inch, const int ouch){ ssize_t todo; for (;;) { ssize_t got = read(inch, buf, bufsize); + //xx cerr << "slurp: read returns " << got << endl; if (got == 0) { // EoF break; } @@ -210,6 +212,7 @@ void slurp(const int inch, const int ouch){ todo = got; while (todo) { ssize_t sent = write(ouch, buf, todo); + //xx cerr << "slurp: write returns " << sent << endl; if (sent < 0 && errno != EINTR) { fprintf(stderr, "hi-q: output error on fd%d : ", ouch); perror(0); @@ -282,6 +285,22 @@ string basename(const string path){ return path; } +void attach(const int pipe_end, const int fd, const int kidno){ + cerr << "attaching current pipe_end " << pipe_end + << " to " << fd + << " for " << kidno << endl; + if (pipe_end != fd) { + int rslt = dup2(pipe_end, fd); + if (rslt < 0) { + fprintf(stderr, "hi-q: dup2(%d,%d) failed for kid %d : ", pipe_end, fd, kidno); + perror(0); + exit(ex_syserr); + } + close(pipe_end); + } + +} + int main(int argc, char** argv) { { progname = *argv; @@ -299,7 +318,6 @@ bar int kidstatus; int rslt; - int loose_end = 0; // our original stdin typedef vector VS; vector filter; @@ -395,11 +413,28 @@ bar // to close it and dup() something useful onto it. map iiofpid; - + map next_read; + next_read[MSG] = 0; // our original stdin + next_read[ENV] = -1; // no kid is (yet) empowered to read envelope info + int slurp_read(1); // our original non-standard input + int slurp_write = -1; // effectively next_write[ENV]; + map current_read; + map cur_write; // current kid writes here + cur_write[MSG] = -1; + cur_write[ENV] = -1; + +// important loop to start all kids for (unsigned int ii=0; ii < nkids; ii++){ /* loop starting all kids */ - //xx cerr << "top of loop ... loose end " << loose_end << " for " << ii << endl; - if (loose_end > 20) exit(99); - int kid_end; + current_read = next_read; + + cerr << "top of loop: " + << " cr.MSG: " << current_read[MSG] + << " cr.ENV: " << current_read[ENV] + << " w.MSG: " << cur_write[MSG] + << " w.ENV: " << cur_write[ENV] + << " for " << ii << endl; + if (current_read[MSG] > 20) exit(99); + if (current_read[ENV] > 20) exit(99); int datapipe[2]; @@ -407,17 +442,15 @@ bar case series: case qq: case sa: -// connect *old* loose end to this kid's stdin - //xx cerr << "moving old loose end " << loose_end << " to 0 for " << ii << endl; - if (loose_end) { - close(0); - dup2(loose_end, 0); - close(loose_end); - } -// Create a pipe, which will be used to connect -// this child's fd1 to the next child's fd0 ... -// except for the last kid, which reads both fd0 and fd1, +// Create a new pipe. +// Pipe must be created here (in the parent). +// The intended bindings must be figured out shortly below. +// Some of the bindings must be hooked up later (in the child), +// while others are used by the parent (e.g. envelope slurp). +// This pipe will be used (by the children) to connect +// this child's output to the next child's input ... +// except for the special kid, which reads both fd0 and fd1, // while writing nothing. rslt = pipe(datapipe); if (rslt < 0) { @@ -425,6 +458,10 @@ bar perror(0); exeunt(ex_syserr); } + if (1) cerr << "new pipe" + << " reading: " << datapipe[rEnd] + << " writing: " << datapipe[wEnd] + << endl; break; case postspam: case stub: @@ -435,20 +472,23 @@ bar exeunt(ex_syserr); } -// For N-1 kids, the loose end feeds forward. -// It will be written by this kid and read by the next kid. -// For the special kid, the loose end will be its nonstandard input. -// It will be written by us (hi-q) and read by the last kid. - +// figure out the intended bindings: switch (filter[ii].mode) { - case series: case sa: - loose_end = datapipe[rEnd]; - kid_end = datapipe[wEnd]; + case series: + cur_write[MSG] = datapipe[wEnd]; + next_read[MSG] = datapipe[rEnd]; break; case qq: - loose_end = datapipe[wEnd]; // reverse of normal "series" case - kid_end = datapipe[rEnd]; // reverse of normal "series" case + if (slurp_write >= 0){ + cerr << "???? multiple qq jobs?" << endl; + } + slurp_write= datapipe[wEnd]; + current_read[ENV] = datapipe[rEnd]; + next_read[ENV] = -1; + next_read[MSG] = -1; + cur_write[ENV] = -1; + cur_write[MSG] = -1; break; case postspam: case stub: @@ -467,7 +507,7 @@ bar } iiofpid[kidpid[ii]] = ii; if (!kidpid[ii]) { /*** child code ***/ - if (verbose) cerr << "top of kid ... loose end " << loose_end << " for " << ii << endl; + if (verbose) cerr << "top of kid ... loose end " << current_read[MSG] << " for " << ii << endl; pid_t kidgroup(0); // process group for all kids is // equal to pid of kid#0 @@ -513,31 +553,19 @@ bar } } - if (0) cerr << "before closing loose end " << loose_end - << " and kid end " << kid_end - << " for " << ii << endl; switch (filter[ii].mode){ - case sa: case qq: + attach(current_read[MSG], 0, ii); + attach(current_read[ENV], 1, ii); + break; + case sa: case series: - close(loose_end); // the reading end is none of this kid's business - // except last kid: writing end - - // Note this does an implicit close on the previously-open fd1: - rslt = dup2(kid_end, 1); // the writing end is stdout for this kid - // except last kid: nonstandard input - if (rslt < 0) { - fprintf(stderr, "hi-q: kid %d: dup2(%d,1) failed: ", ii, kid_end); - perror(0); - exit(ex_syserr); - } - close(kid_end); // use fd1 instead now - // OK, at this point this kid is set up to read fd0 and write fd1 - // (except last kid reads fd1 as well as fd0). + attach(current_read[MSG], 0, ii); + attach(cur_write[MSG], 1, ii); break; case stub: case postspam: - // nothing to do + // nothing to hook up; no pipe was even created. break; case fail: cerr << "should never happen: invalid filter" << endl; @@ -545,6 +573,12 @@ bar break; } +// in all modes: +// close envelope channel in kid space +// (leaving it open in parent space) + close(current_read[ENV]); + close(slurp_write); + //// probe_fd(); int ntok = filter[ii].cmd.size(); @@ -576,7 +610,10 @@ bar perror(0); exeunt(ex_syserr); } - close(kid_end); + +// these tricks are for kid: + close(cur_write[MSG]); + close(cur_write[ENV]); // Let kid #0 run a little ways: if (ii==0) { @@ -598,6 +635,7 @@ bar } /* end loop starting all kids */ // here with the whole pipeline of kids launched +// parent program continues close(resync[wEnd]); // important, so that block gets released close(resync[rEnd]); // less important, just housecleaning @@ -730,35 +768,46 @@ bar // Here if all filters agree this is not spam. // Now it is safe to transfer the envelope information: - slurp(1, loose_end); - close(1); - close(loose_end); + + if (0) cerr << "about to slurp: " + << " cr.MSG: " << current_read[MSG] + << " cr.ENV: " << current_read[ENV] + << " w.MSG: " << cur_write[MSG] + << " w.ENV: " << cur_write[ENV] + << " slurp_read: " << slurp_read + << " slurp_write: " << slurp_write + << endl; + + slurp(slurp_read, slurp_write); + close(slurp_write); + close(slurp_read); // now that the envelope information has been transfered, // wait for the last kid in the usual way - { - for(;;) { - waitpid(special_pid, &kidstatus, WUNTRACED); - if (WIFEXITED(kidstatus)) { - int sts = WEXITSTATUS(kidstatus); - cerr << progid - << " says: qq program" - << " i.e. " << basename(filter[nkids-1].cmd[0]) - << "[" << kidpid[nkids-1] << "]" - << " returned status " << sts - << endl; - return sts; - } else if (WIFSIGNALED(kidstatus)) { - cerr << progid - << " says: qq program" - << " i.e. " << basename(filter[nkids-1].cmd[0]) - << "[" << kidpid[nkids-1] << "]" - << " was killed by signal " << WTERMSIG(kidstatus) - << endl; - return ex_syserr; - } else { - /* paused, not dead */ - } + + for(;;) { + waitpid(special_pid, &kidstatus, WUNTRACED); + if (WIFEXITED(kidstatus)) { + int sts = WEXITSTATUS(kidstatus); + cerr << progid + << " says: qq program" + << " i.e. " << basename(filter[nkids-1].cmd[0]) + << "[" << kidpid[nkids-1] << "]" + << " returned status " << sts + << endl; + return sts; + } else if (WIFSIGNALED(kidstatus)) { + cerr << progid + << " says: qq program" + << " i.e. " << basename(filter[nkids-1].cmd[0]) + << "[" << kidpid[nkids-1] << "]" + << " was killed by signal " << WTERMSIG(kidstatus) + << endl; + return ex_syserr; + } else { + /* paused, not dead */ } - } + } /* loop until all kids accounted for */ + // should never get here; + // exit from within loop is the only way out } diff --git a/tools/hi-test.c b/tools/hi-test.c index e2626cc..0661ada 100644 --- a/tools/hi-test.c +++ b/tools/hi-test.c @@ -3,6 +3,7 @@ #include #include #include +#include #include /* perror() */ @@ -13,6 +14,8 @@ const int sa_good(0); const int sa_spam(1); const int sa_usage(64); +int verbosity(0); + //////////////// // little utility to help with argument parsing: // @@ -42,10 +45,12 @@ void exeunt(const int sts){ using namespace std; string progname; +string progid; +int mypid; void dump(const string var){ char* str = getenv(var.c_str()); - cerr << progname << ": " << var; + cerr << progid << " " << var; if (str) cerr << " is set to '" << str << "'" << endl; else cerr << " is not set." << endl; } @@ -55,10 +60,19 @@ void countsome(const int unit){ int total(0); for (;;) { int rslt = read(unit, buf, sizeof(buf)); + if (verbosity) cerr << "hi-test: count: unit " << unit + << " read returns " << rslt << endl; if (rslt <= 0) break; total += rslt; } - cerr << "read " << total << " bytes from unit " << unit << endl; + cerr << progid + << " 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){ @@ -68,7 +82,16 @@ int main(int _argc, const char** _argv){ int countmode(0); int argc(_argc); const char **argv(_argv); - progname = *argv; argv++; argc--; + + { + progname = *argv; + mypid = getpid(); + stringstream binder; + binder << "+++++ " << basename(progname) << "[" << mypid << "]"; + progid = binder.str(); + } + + argv++; argc--; while (argc) { string arg(*argv); argv++; argc--; @@ -113,8 +136,8 @@ int main(int _argc, const char** _argv){ exit(sa_usage); } } - - cerr << "++++ hi-test pid: " << getpid() << " group: " << getpgid(0); + + cerr << progid << " group: " << getpgid(0); char* foo = getenv("HI_Q_GROUP"); if (foo) cerr << " HI_Q_GROUP: " << foo; cerr << endl; diff --git a/tools/hi-test.conf b/tools/hi-test.conf index f692f37..20df5a7 100755 --- a/tools/hi-test.conf +++ b/tools/hi-test.conf @@ -1,6 +1,7 @@ -#! /usr/local/bin/bash-c set -x ; Date: Mon, 23 Jul 2012 14:31:05 -0700 Subject: finally () get smart about not leaving stray FDs lying around; also get smart about blocking fd0 and fd1 for later use. --- tools/hi-q.c | 119 ++++++++++++++++++++++++++++++++++++++++------------ tools/hi-test4.conf | 2 +- 2 files changed, 93 insertions(+), 28 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 5ee7688..3d654c5 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -22,10 +22,15 @@ using namespace std; #include #include #include +#include #include #include #include +#include /* for fstat */ +#include /* .. */ +#include /* .. */ + // error exit codes, mostly as stated in qmail.c #define bar \ foo(good, 0) ;\ @@ -63,6 +68,13 @@ foo_sa(NOPERM, 77, "permission denied") ;\ foo_sa(CONFIG, 78, "configuration error") ;\ foo_sa(TOOBIG, 98, "message was too big to process (see --max-size)" +string progname; +pid_t mypid; +string progid; + +extern char** environ; +const int rEnd(0); // end of a pipe for reading +const int wEnd(1); // end of a pipe for writing typedef enum {MSG, ENV} channeler; @@ -204,7 +216,9 @@ void slurp(const int inch, const int ouch){ break; } if (got < 0) { - fprintf(stderr, "hi-q: input error: "); + cerr << progid + << " slurp: input error on fd " << inch + << " : "; perror(0); exeunt(ex_comerr); } @@ -214,7 +228,9 @@ void slurp(const int inch, const int ouch){ ssize_t sent = write(ouch, buf, todo); //xx cerr << "slurp: write returns " << sent << endl; if (sent < 0 && errno != EINTR) { - fprintf(stderr, "hi-q: output error on fd%d : ", ouch); + cerr << progid + << " slurp: output rror on fd " << ouch + << " : "; perror(0); exeunt(ex_comerr); } @@ -261,10 +277,6 @@ void usage() { // we have data coming in on fd 0. // and envelope / control information coming in on fd 1. -string progname; -pid_t mypid; -string progid; - void dump(const string var){ char* str = getenv(var.c_str()); cerr << progid << var; @@ -277,7 +289,39 @@ int xclose(int arg){ return close(arg); } -extern char** environ; +typedef list LI; +void block_fd(const LI todo){ + int blocker(-1); + int inplace(0); + + for (LI::const_iterator ptr = todo.begin(); + ptr != todo.end(); ptr++) { + int fd = *ptr; + struct stat statbuf; + int rslt = fstat(fd, &statbuf); + if (rslt) { + if (0) { + cerr << "definitely needed to block_fd unit " << fd << " : "; + perror(0); + } + if (blocker < 0) { + int blockex[2]; + pipe(blockex); + close(blockex[rEnd]); + blocker = blockex[wEnd]; + } + if (blocker != fd){ + dup2(blocker, fd); + close(blocker); + } else { + inplace++; + } + } else { + if (0) cerr << "unit " << fd << " already blocked" << endl; + } + } + if (!inplace) close(blocker); +} string basename(const string path){ size_t where = path.rfind("/"); @@ -286,7 +330,7 @@ string basename(const string path){ } void attach(const int pipe_end, const int fd, const int kidno){ - cerr << "attaching current pipe_end " << pipe_end + cerr << "attaching current pipe_end " << pipe_end << " to " << fd << " for " << kidno << endl; if (pipe_end != fd) { @@ -298,7 +342,7 @@ void attach(const int pipe_end, const int fd, const int kidno){ } close(pipe_end); } - + } int main(int argc, char** argv) { @@ -397,8 +441,6 @@ bar vector kidpid(nkids); // indexed by kid number - const int rEnd(0); // end of a pipe for reading - const int wEnd(1); // end of a pipe for writing int sync[2]; int resync[2]; if (pipe(sync) != 0) cerr << "sync pipe failed" << endl; @@ -419,19 +461,30 @@ bar int slurp_read(1); // our original non-standard input int slurp_write = -1; // effectively next_write[ENV]; map current_read; - map cur_write; // current kid writes here - cur_write[MSG] = -1; - cur_write[ENV] = -1; + map current_write; // current kid writes here + current_write[MSG] = -1; + current_write[ENV] = -1; + list blockme; + blockme.push_back(0); + blockme.push_back(1); // important loop to start all kids for (unsigned int ii=0; ii < nkids; ii++){ /* loop starting all kids */ + string kidid; + { + stringstream foo; + foo << ii + << " mode " << filter[ii].mode + << " " << filter[ii].cmd[0]; + kidid = foo.str(); + } current_read = next_read; cerr << "top of loop: " - << " cr.MSG: " << current_read[MSG] + << " cr.MSG: " << current_read[MSG] << " cr.ENV: " << current_read[ENV] - << " w.MSG: " << cur_write[MSG] - << " w.ENV: " << cur_write[ENV] + << " w.MSG: " << current_write[MSG] + << " w.ENV: " << current_write[ENV] << " for " << ii << endl; if (current_read[MSG] > 20) exit(99); if (current_read[ENV] > 20) exit(99); @@ -452,6 +505,7 @@ bar // this child's output to the next child's input ... // except for the special kid, which reads both fd0 and fd1, // while writing nothing. + block_fd(blockme); rslt = pipe(datapipe); if (rslt < 0) { fprintf(stderr, "hi-q: could not create datapipe: "); @@ -473,10 +527,13 @@ bar } // figure out the intended bindings: + int pardang1(-1), pardang2(-1); // used by current kid, + // but dangling, from parent's point of view switch (filter[ii].mode) { case sa: case series: - cur_write[MSG] = datapipe[wEnd]; + pardang1 = current_write[MSG] = datapipe[wEnd]; + pardang2 = current_read[MSG]; next_read[MSG] = datapipe[rEnd]; break; case qq: @@ -484,11 +541,11 @@ bar cerr << "???? multiple qq jobs?" << endl; } slurp_write= datapipe[wEnd]; - current_read[ENV] = datapipe[rEnd]; + pardang1 = current_read[ENV] = datapipe[rEnd]; next_read[ENV] = -1; next_read[MSG] = -1; - cur_write[ENV] = -1; - cur_write[MSG] = -1; + current_write[ENV] = -1; + current_write[MSG] = -1; break; case postspam: case stub: @@ -561,7 +618,7 @@ bar case sa: case series: attach(current_read[MSG], 0, ii); - attach(cur_write[MSG], 1, ii); + attach(current_write[MSG], 1, ii); break; case stub: case postspam: @@ -612,8 +669,16 @@ bar } // these tricks are for kid: - close(cur_write[MSG]); - close(cur_write[ENV]); + close(current_write[MSG]); + close(current_write[ENV]); + close(current_read[ENV]); + if (0) cerr << "closing " << pardang1 + << " for parent of " << kidid << endl; + + close(pardang1); + if (0) cerr << "closing " << pardang2 + << " for parent of " << kidid << endl; + close(pardang2); // Let kid #0 run a little ways: if (ii==0) { @@ -770,10 +835,10 @@ bar // Now it is safe to transfer the envelope information: if (0) cerr << "about to slurp: " - << " cr.MSG: " << current_read[MSG] + << " cr.MSG: " << current_read[MSG] << " cr.ENV: " << current_read[ENV] - << " w.MSG: " << cur_write[MSG] - << " w.ENV: " << cur_write[ENV] + << " w.MSG: " << current_write[MSG] + << " w.ENV: " << current_write[ENV] << " slurp_read: " << slurp_read << " slurp_write: " << slurp_write << endl; diff --git a/tools/hi-test4.conf b/tools/hi-test4.conf index 850784e..c0ef589 100755 --- a/tools/hi-test4.conf +++ b/tools/hi-test4.conf @@ -5,7 +5,7 @@ #! /usr/local/bin/bash-c set -x ; /bin/echo "a b c " | 1 Date: Mon, 23 Jul 2012 14:43:34 -0700 Subject: minor polishing --- tools/hi-q.c | 8 ++++---- tools/hi-test4.conf | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 3d654c5..d267d2c 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -301,7 +301,7 @@ void block_fd(const LI todo){ int rslt = fstat(fd, &statbuf); if (rslt) { if (0) { - cerr << "definitely needed to block_fd unit " << fd << " : "; + cerr << "**** definitely needed to block_fd unit " << fd << " : "; perror(0); } if (blocker < 0) { @@ -330,7 +330,7 @@ string basename(const string path){ } void attach(const int pipe_end, const int fd, const int kidno){ - cerr << "attaching current pipe_end " << pipe_end + if (0) cerr << "attaching current pipe_end " << pipe_end << " to " << fd << " for " << kidno << endl; if (pipe_end != fd) { @@ -480,7 +480,7 @@ bar } current_read = next_read; - cerr << "top of loop: " + if (verbose) cerr << "top of loop: " << " cr.MSG: " << current_read[MSG] << " cr.ENV: " << current_read[ENV] << " w.MSG: " << current_write[MSG] @@ -512,7 +512,7 @@ bar perror(0); exeunt(ex_syserr); } - if (1) cerr << "new pipe" + if (0) cerr << "new pipe" << " reading: " << datapipe[rEnd] << " writing: " << datapipe[wEnd] << endl; diff --git a/tools/hi-test4.conf b/tools/hi-test4.conf index c0ef589..394279c 100755 --- a/tools/hi-test4.conf +++ b/tools/hi-test4.conf @@ -8,4 +8,9 @@ series /bin/echo "a b c" sa /bin/cat stub hi-test x0 -snooze 1 series /bin/cat +series /bin/cat +series /bin/cat +series /bin/cat +series /bin/cat +series /bin/cat qq hi-test -count -- cgit v1.2.3 From 3993d2f92fc1d357ee668d42cbb44aa3744e6d2c Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 23 Jul 2012 14:52:21 -0700 Subject: more minor polishing --- tools/hi-q.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index d267d2c..502de69 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -527,13 +527,13 @@ bar } // figure out the intended bindings: - int pardang1(-1), pardang2(-1); // used by current kid, - // but dangling, from parent's point of view + list pardang; switch (filter[ii].mode) { case sa: case series: - pardang1 = current_write[MSG] = datapipe[wEnd]; - pardang2 = current_read[MSG]; + current_write[MSG] = datapipe[wEnd]; + pardang.push_back(current_write[MSG]); + pardang.push_back(current_read[MSG]); next_read[MSG] = datapipe[rEnd]; break; case qq: @@ -541,7 +541,8 @@ bar cerr << "???? multiple qq jobs?" << endl; } slurp_write= datapipe[wEnd]; - pardang1 = current_read[ENV] = datapipe[rEnd]; + current_read[ENV] = datapipe[rEnd]; + pardang.push_back(current_read[ENV]); next_read[ENV] = -1; next_read[MSG] = -1; current_write[ENV] = -1; @@ -672,13 +673,13 @@ bar close(current_write[MSG]); close(current_write[ENV]); close(current_read[ENV]); - if (0) cerr << "closing " << pardang1 - << " for parent of " << kidid << endl; + for (LI::const_iterator ptr = pardang.begin(); + ptr != pardang.end(); ptr++) { + if (0) cerr << "closing " << *ptr + << " for parent of " << kidid << endl; - close(pardang1); - if (0) cerr << "closing " << pardang2 - << " for parent of " << kidid << endl; - close(pardang2); + close(*ptr); + } // Let kid #0 run a little ways: if (ii==0) { -- cgit v1.2.3 From 9b5fbc11bb1d96dd598ebfad5539660f75571835 Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 23 Jul 2012 18:01:34 -0700 Subject: progress toward cleaning up skrewt --- tools/skrewt.c | 265 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 163 insertions(+), 102 deletions(-) diff --git a/tools/skrewt.c b/tools/skrewt.c index 6de3dd9..ed0e627 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -12,6 +12,7 @@ #include /* perror */ #include +#include using namespace std; @@ -144,6 +145,58 @@ string basename(const string path){ string progname, progid; int mypid; + +/* Content-Type: text/plain; charset="us-ascii" */ +/* Content-Type: multipart/mixed; boundary="1170861315-1262462055-1341954763=:92165" */ +void parse_content(const string type_spec_line, string &maintype, string &boundary) { + string mainline(type_spec_line); + + string get_type(toLower(mainline)); + size_t where = get_type.find_first_of(" \t;\n"); + if (where == string::npos) { + // keep whole string + } + else { + get_type = get_type.substr(0,where); + } + where = get_type.find("/"); + if (where == string::npos){ + maintype = ""; + } else { + maintype = get_type.substr(0, where); + } +} + +#ifdef xxxxxxxxxxxxxxxxxxxx + if + if (0) cerr << "type (" << get_type << ") " + << (text_type?"text":"nope") << endl; + + + string srch = "boundary="; + where = headrec.find(srch); + if (where != string::npos) { + where += srch.length(); + boundary = headrec.substr(where); + if (boundary[0] == '"') { + boundary = boundary.substr(1); + where = boundary.find_first_of("\""); + } else { + where = boundary.find_first_of(" \t;\n"); + } + if (where == string::npos) { + /* do nothing, boundary=boundary as a whole */ + } else { + boundary = boundary.substr(0, where); + } + } + } +#endif + + + + + //////////////////////////////////////////////////////////// int main(int _argc, const char** _argv){ //// pid_t pid = getpid(); @@ -159,8 +212,7 @@ int main(int _argc, const char** _argv){ progid = binder.str(); } - - int maxsize(1000000); + int maxsize(1000*1000); while (argc) { string arg(*argv); argv++; argc--; @@ -186,121 +238,130 @@ int main(int _argc, const char** _argv){ } } - int inheads(1); + int saw_blank_line(0); string boundary("x-xx-x"); - int text_type(1); - int textlines(0); - int gotdate(0); + string date; + string subject; + string content_type; + string message_id; int msgsize(0); - for (;;){ + vector bigbuf; + cerr << "hi there" << endl; + + for (;;){ // outer loop over all records in the header if (cin.eof()) break; if (cin.bad()) return 1; - if (inheads) { - string header; - if (getline(cin, header).fail()) continue; - msgsize += header.length()+1; + + string headrec; +// on fail, go back to top of outer loop and check for eof versus bad + if (getline(cin, headrec).fail()) continue; + msgsize += headrec.length()+1; + if (msgsize > maxsize) { + cerr << progid << " rejection: bigger than " << maxsize << endl; + exeunt(ex_spam); + } + cout << headrec << endl; + bigbuf.push_back(headrec); // for a folded record, this is the first line + + for (;;) { // inner loop to build a multi-line record e.g. folded record: + if (cin.eof()) break; + if (cin.bad()) return 1; + char ch; + if (cin.get(ch).fail()) continue; + cin.putback(ch); + if (ch != ' ' && ch != '\t') break; + string line; +// on fail, go back to top of inner loop and check for eof versus bad + if (getline(cin, line).fail()) continue; + msgsize += line.length()+1; if (msgsize > maxsize) { cerr << progid << " rejection: bigger than " << maxsize << endl; exeunt(ex_spam); } - for (;;) { - if (cin.eof()) break; - if (cin.bad()) return 1; - char ch; - if (cin.get(ch).fail()) continue; - cin.putback(ch); - if (ch != ' ' && ch != '\t') break; - string line; - if (getline(cin, line).fail()) continue; - msgsize += line.length()+1; - if (msgsize > maxsize) { - cerr << progid << " rejection: bigger than " << maxsize << endl; - exeunt(ex_spam); - } - header += "\n" + line; - } - int len = header.length(); - if (len && header[len-1] == '\r') len--; // reduced length, not counting - if (len == 0) { - if (!gotdate) { - cerr << progid << " rejection: no date" << endl; - exeunt(ex_spam); // disallow mail with no date - } - inheads = 0; - //cerr << "end of headers" << endl; + cout << line << endl; + bigbuf.push_back(line); + string cooked(line); + if (cooked.length()){ + string::iterator ptr = cooked.end()-1; + if (*ptr == '\r') cooked.erase(ptr); } - else { + headrec += "\n" + cooked; + } +// here with a fully assembled header record + 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 + } + // here if it's a header line - string headword; - string rest; - size_t where = header.find(":"); - if (where != string::npos) { - headword = header.substr(0, where); - rest = ltrim(header.substr(1+where)); - } - headword = toLower(headword); - if (headword == "content-type") { - string the_type = rest; - size_t where = the_type.find_first_of(" \t;\n"); - if (where == string::npos) { - /* do nothing */ - } - else { - the_type = the_type.substr(0,where); - } - the_type = toLower(the_type); - text_type = (the_type.find("text/") == 0); - if (0) cerr << "type (" << the_type << ") " - << (text_type?"text":"nope") << endl; - string srch = "boundary="; - where = header.find(srch); - if (where != string::npos) { - where += srch.length(); - boundary = header.substr(where); - if (boundary[0] == '"') { - boundary = boundary.substr(1); - where = boundary.find_first_of("\""); - } else { - where = boundary.find_first_of(" \t;\n"); - } - if (where == string::npos) { - /* do nothing, boundary=boundary as a whole */ - } else { - boundary = boundary.substr(0, where); - } - } - } else if (headword == "date") { - gotdate++; - } else if (headword == "subject") { - if (rest.find("-please-bounce-this-") != string::npos) { - cerr << progid << " rejection: by request" << endl; - exeunt(ex_spam); - } - } - } - //xxxx cout << header.length() << " ... "; - cout << header << endl; + string headword; + string rest; + size_t where = headrec.find(":"); + if (where != string::npos) { + headword = headrec.substr(0, where); + rest = ltrim(headrec.substr(1+where)); + } + headword = toLower(headword); + if (0){ + } else if (headword == "date") { + date = rest; + } else if (headword == "subject") { + subject = rest; + } else if (headword == "content-type") { + content_type = rest; + } + //xxxx cout << headrec.length() << " ... "; + } + cerr << "headers are done. Delimited: " << saw_blank_line << endl; + +// Headers are done. +// Do some early-stage thinking. + + if (subject.find("-please-bounce-this-") != string::npos) { + cerr << progid << " rejection: by request" << endl; + exeunt(ex_spam); + } + + if (!date.length()) { + cerr << progid << " rejection: no date" << endl; + exeunt(ex_spam); // disallow mail with no date + } + + string contype; + int textlines(0); + parse_content(content_type, contype, boundary); + int is_text = contype == "text"; + +// early-stage thinking has been done. +// Now spew the rest of the message + cerr << "body begins: " << contype << " " << is_text << endl; + int inheads(0); + for (;;){ // outer loop over all lines in the body + if (cin.eof()) break; + if (cin.bad()) return 1; + string line; +// on fail, go back to top of outer loop and check for eof versus bad + if (getline(cin, line).fail()) continue; + msgsize += line.length()+1; + if (msgsize > maxsize) { + cerr << progid << " rejection: bigger than " << maxsize << endl; + exeunt(ex_spam); + } + bigbuf.push_back(line); + cout << line << endl; + if (line == "--" + boundary) { + inheads = 1; } else { - string line; - if (!getline(cin, line).fail()) { - msgsize += line.length()+1; - if (msgsize > maxsize) { - cerr << progid << " rejection: bigger than " << maxsize << endl; - exeunt(ex_spam); - } - if (line == "--" + boundary) { - inheads = 1; - } else { - if (text_type) { - if (ltrim(line).length()) textlines++; - } - } - cout << line << endl; + if (is_text) { + if (ltrim(line).length()) textlines++; } } } + if (0) cerr << "textlines: " << textlines << endl; - if (!textlines) { + if (000 && !textlines) { cerr << progid << " rejection: no text" << endl; exeunt(ex_spam); } -- cgit v1.2.3 From 0f0fc6dbb7e7354ac662951f124b0a64c54b741b Mon Sep 17 00:00:00 2001 From: John Denker Date: Mon, 23 Jul 2012 21:37:35 -0700 Subject: get line-counter to work --- tools/skrewt.c | 134 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 57 deletions(-) diff --git a/tools/skrewt.c b/tools/skrewt.c index ed0e627..d2e1bbc 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -145,56 +145,52 @@ string basename(const string path){ string progname, progid; int mypid; - /* Content-Type: text/plain; charset="us-ascii" */ /* Content-Type: multipart/mixed; boundary="1170861315-1262462055-1341954763=:92165" */ +// void parse_content(const string type_spec_line, string &maintype, string &boundary) { - string mainline(type_spec_line); - - string get_type(toLower(mainline)); - size_t where = get_type.find_first_of(" \t;\n"); - if (where == string::npos) { - // keep whole string - } - else { - get_type = get_type.substr(0,where); - } - where = get_type.find("/"); - if (where == string::npos){ - maintype = ""; - } else { - maintype = get_type.substr(0, where); - } -} - -#ifdef xxxxxxxxxxxxxxxxxxxx - if - if (0) cerr << "type (" << get_type << ") " - << (text_type?"text":"nope") << endl; - - - string srch = "boundary="; - where = headrec.find(srch); - if (where != string::npos) { - where += srch.length(); - boundary = headrec.substr(where); - if (boundary[0] == '"') { - boundary = boundary.substr(1); - where = boundary.find_first_of("\""); - } else { - where = boundary.find_first_of(" \t;\n"); - } - if (where == string::npos) { - /* do nothing, boundary=boundary as a whole */ - } else { - boundary = boundary.substr(0, where); - } - } - } -#endif + cerr << "parser called with: " << type_spec_line << endl; + string get_type(type_spec_line); + size_t where = get_type.find_first_of(" \t;\n"); + string rest; + if (where == string::npos) { + // keep whole string + } + else { + rest = get_type.substr(where+1); + get_type = get_type.substr(0,where); + } + where = get_type.find("/"); + if (where == string::npos){ + maintype = ""; + cerr << "could not find / in " << get_type << endl; + } else { + maintype = get_type.substr(0, where); + } +// now need to find boundary + string srch = "boundary="; + where = rest.find(srch); + if (where != string::npos) { + where += srch.length(); + boundary = rest.substr(where); + if (boundary[0] == '"') { + boundary = boundary.substr(1); + where = boundary.find_first_of("\""); + } else { + where = boundary.find_first_of(" \t;\n"); + } + if (where == string::npos) { + /* do nothing, boundary=boundary as a whole */ + } else { + boundary = boundary.substr(0, where); + } + } else { + //xxxxxxx cerr << "boundary= not found in " << type_spec_line << endl; + } +} //////////////////////////////////////////////////////////// @@ -329,15 +325,17 @@ int main(int _argc, const char** _argv){ exeunt(ex_spam); // disallow mail with no date } - string contype; - int textlines(0); - parse_content(content_type, contype, boundary); - int is_text = contype == "text"; + string main_contype; + parse_content(content_type, main_contype, boundary); + int currently_text = main_contype == "text"; + int main_multipart = main_contype == "multipart"; // early-stage thinking has been done. // Now spew the rest of the message - cerr << "body begins: " << contype << " " << is_text << endl; - int inheads(0); + cerr << "body begins: " << main_contype << " " << currently_text << " " << boundary << endl; + int in_subheads(0); + int textlines(0); + for (;;){ // outer loop over all lines in the body if (cin.eof()) break; if (cin.bad()) return 1; @@ -351,19 +349,41 @@ int main(int _argc, const char** _argv){ } bigbuf.push_back(line); cout << line << endl; - if (line == "--" + boundary) { - inheads = 1; + if (in_subheads){ + if (line == "" || line == "\r") in_subheads = 0; + } + if (in_subheads){ + string sub_contype; + string junk; +// in principle could worry about folded headers, +// but in this application it doesn't actually matter + string headword; + string rest; + size_t where = line.find(":"); + if (where != string::npos) { + headword = line.substr(0, where); + rest = ltrim(line.substr(1+where)); + } + headword = toLower(headword); + if (headword == "content-type") { + parse_content(rest, sub_contype, junk); + currently_text = sub_contype == "text"; + cerr << "setting contype '" << sub_contype << "' " << currently_text << " ... " << textlines << endl; + } } else { - if (is_text) { - if (ltrim(line).length()) textlines++; + if (main_multipart && line == "--" + boundary) { + //xxxx cerr << "found subhead boundary" << endl; + in_subheads = 1; + continue; } + if (currently_text) textlines++; } } - if (0) cerr << "textlines: " << textlines << endl; - if (000 && !textlines) { + if (1) cerr << "textlines: " << textlines << endl; + if (1 && !textlines) { cerr << progid << " rejection: no text" << endl; - exeunt(ex_spam); +// exeunt(ex_spam); } cerr << progid << " normal completion" << endl; exit(ex_good); -- cgit v1.2.3 From e2390efc92d66a50c6ce13d027aac8c6ecc02e89 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 24 Jul 2012 11:28:34 -0700 Subject: remove print statements --- tools/skrewt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/skrewt.c b/tools/skrewt.c index d2e1bbc..ad99e67 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -149,7 +149,7 @@ int mypid; /* Content-Type: multipart/mixed; boundary="1170861315-1262462055-1341954763=:92165" */ // void parse_content(const string type_spec_line, string &maintype, string &boundary) { - cerr << "parser called with: " << type_spec_line << endl; + //xxx cerr << "parser called with: " << type_spec_line << endl; string get_type(type_spec_line); size_t where = get_type.find_first_of(" \t;\n"); @@ -310,7 +310,7 @@ int main(int _argc, const char** _argv){ } //xxxx cout << headrec.length() << " ... "; } - cerr << "headers are done. Delimited: " << saw_blank_line << endl; + //xxx cerr << "headers are done. Delimited: " << saw_blank_line << endl; // Headers are done. // Do some early-stage thinking. @@ -368,7 +368,7 @@ int main(int _argc, const char** _argv){ if (headword == "content-type") { parse_content(rest, sub_contype, junk); currently_text = sub_contype == "text"; - cerr << "setting contype '" << sub_contype << "' " << currently_text << " ... " << textlines << endl; + //xxxx cerr << "setting contype '" << sub_contype << "' " << currently_text << " ... " << textlines << endl; } } else { if (main_multipart && line == "--" + boundary) { -- 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(-) 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 6e8083ff4ffe3fd2b6d337386637a2b5c1378cf7 Mon Sep 17 00:00:00 2001 From: John Denker Date: Wed, 25 Jul 2012 13:05:03 -0700 Subject: fix a bunch of DOS-CR bugs --- tools/filters.conf | 2 +- tools/mail-scan.c | 12 ++++++ tools/skrewt.c | 121 +++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 102 insertions(+), 33 deletions(-) diff --git a/tools/filters.conf b/tools/filters.conf index bd8eb33..3cbd5bf 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -1,5 +1,5 @@ # configuration file for hi-q -series /var/qmail/bin/skrewt +series /var/qmail/bin/skrewt -err stub /var/qmail/bin/greylist -check -v sa /usr/local/bin/spamc -Y 0 -s 1000000 -x qq /var/qmail/bin/qmail-queue diff --git a/tools/mail-scan.c b/tools/mail-scan.c index 1ea245f..dc8aa5c 100644 --- a/tools/mail-scan.c +++ b/tools/mail-scan.c @@ -183,6 +183,16 @@ public: } }; +string noCR(const string bar){ + string foo(bar); + int len = foo.length(); + if (len){ + if (foo[len-1] == '\r') { + foo.erase(len-1); + } + } + return foo; +} //////////////////////////////////////////////////////////// int main(int _argc, const char** _argv){ @@ -268,6 +278,7 @@ int main(int _argc, const char** _argv){ return 1; } if (getline(infile, line).fail()) continue; + line = noCR(line); Header.push_back(line); msgsize += line.length()+1; if (msgsize > maxsize) { @@ -335,6 +346,7 @@ int main(int _argc, const char** _argv){ break; } } // end loop over matching records in this file + if (vflag && !foundsome_infile) { cout << *file << endl; didprint++; diff --git a/tools/skrewt.c b/tools/skrewt.c index ad99e67..a43fd13 100644 --- a/tools/skrewt.c +++ b/tools/skrewt.c @@ -1,4 +1,4 @@ -/////////////////// +////////////////// // skrewt.c // // scrutinize email @@ -13,6 +13,7 @@ #include /* perror */ #include #include +#include using namespace std; @@ -28,6 +29,7 @@ void usage(const int sts){ " Options\n" " -help print this msg (and exit immediately).\n" " -maxsize ii msg size in bytes; anything bigger will be rejected.\n" +" -error-exit exit early if errors have been detected.\n" "\n" " Messages containing the string '-please-bounce-this-' will be rejected.\n" " Messages with no date will be rejected.\n" @@ -36,7 +38,7 @@ void usage(const int sts){ } // error exit codes, mostly as stated in qmail.c -#define bar \ +#define ErrorCodes \ foo(good, 0) ;\ foo(spam, 21) ;\ foo(permerr, 31) ;\ @@ -46,7 +48,7 @@ foo(syserr, 71) ;\ foo(comerr, 74) ; #define foo(name, num) const int ex_ ## name = num -bar +ErrorCodes #undef foo @@ -104,12 +106,23 @@ string toLower(const std::string& a){ } //////////////// -string ltrim(string foo){ +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(); + if (len){ + if (foo[len-1] == '\r') { + foo.erase(len-1); + } + } + return foo; +} + //////////////// // little utility to help with argument parsing: // @@ -117,7 +130,8 @@ int prefix(const string shorter, const string longer){ return shorter == longer.substr(0, shorter.length()); } -void exeunt(const int sts){ +void maybe_exeunt(const int sts, const int really){ + if (!really) return; if (sts == ex_good) exit(sts); const char* foo = getenv("HI_Q_GROUP"); @@ -136,6 +150,10 @@ void exeunt(const int sts){ exit(sts); } +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); @@ -192,6 +210,15 @@ void parse_content(const string type_spec_line, string &maintype, string &bounda } } +string join(const string sep, const list stuff){ + string rslt; + for (list::const_iterator ptr = stuff.begin(); + ptr != stuff.end(); ptr++){ + if (rslt.length()) rslt += sep; + rslt += *ptr; + } + return rslt; +} //////////////////////////////////////////////////////////// int main(int _argc, const char** _argv){ @@ -209,6 +236,8 @@ int main(int _argc, const char** _argv){ } int maxsize(1000*1000); + int error_exit(0); + int mid_required(0); while (argc) { string arg(*argv); argv++; argc--; @@ -216,14 +245,18 @@ int main(int _argc, const char** _argv){ if (prefix(arg, "-help")) { usage(0); } - if (prefix(arg, "-maxsize")) { + if (0) { + } else if (prefix(arg, "-mid-required")) { + mid_required++; + } else if (prefix(arg, "-error-exit")) { + error_exit++; + } else if (prefix(arg, "-maxsize")) { if (!argc) { cerr << "Option -maxsize requires an argument" << endl; exit(ex_usage); } maxsize = atoi(*argv); argv++; argc--; - } - if (arg.substr(0,1) == "-") { + } else if (arg.substr(0,1) == "-") { cerr << "Unrecognized option '" << arg << "'" << endl; cerr << "For help, try: " << progname << " -help" << endl; exit(ex_usage); @@ -236,28 +269,32 @@ int main(int _argc, const char** _argv){ int saw_blank_line(0); string boundary("x-xx-x"); - string date; + string to; + string from; string subject; - string content_type; + string date; string message_id; + string content_type; int msgsize(0); vector bigbuf; - cerr << "hi there" << endl; + int recno(0); + //xxxx cerr << progid << " begins" << endl; for (;;){ // outer loop over all records in the header if (cin.eof()) break; if (cin.bad()) return 1; - string headrec; + string line; // on fail, go back to top of outer loop and check for eof versus bad - if (getline(cin, headrec).fail()) continue; - msgsize += headrec.length()+1; + if (getline(cin, line).fail()) continue; + msgsize += line.length()+1; if (msgsize > maxsize) { cerr << progid << " rejection: bigger than " << maxsize << endl; exeunt(ex_spam); } - cout << headrec << endl; - bigbuf.push_back(headrec); // for a folded record, this is the first line + cout << line << endl; + bigbuf.push_back(line); + string headrec = noCR(line); // for a folded record, this is the first line for (;;) { // inner loop to build a multi-line record e.g. folded record: if (cin.eof()) break; @@ -276,12 +313,7 @@ int main(int _argc, const char** _argv){ } cout << line << endl; bigbuf.push_back(line); - string cooked(line); - if (cooked.length()){ - string::iterator ptr = cooked.end()-1; - if (*ptr == '\r') cooked.erase(ptr); - } - headrec += "\n" + cooked; + headrec += "\n" + noCR(line); } // here with a fully assembled header record int len = headrec.length(); @@ -301,6 +333,12 @@ int main(int _argc, const char** _argv){ } headword = toLower(headword); if (0){ + } else if (headword == "from") { + from = rest; + } else if (headword == "to") { + to = rest; + } else if (headword == "message-id") { + message_id = rest; } else if (headword == "date") { date = rest; } else if (headword == "subject") { @@ -309,30 +347,49 @@ int main(int _argc, const char** _argv){ content_type = rest; } //xxxx cout << headrec.length() << " ... "; + recno++; + if (0) if (recno <= 6) cerr << progid << "#" << recno + << " " << headrec << endl; } - //xxx cerr << "headers are done. Delimited: " << saw_blank_line << endl; + cerr << progid <<" Mid '" << message_id << "'" << endl; // Headers are done. // Do some early-stage thinking. + list badnews; + if (subject.find("-please-bounce-this-") != string::npos) { - cerr << progid << " rejection: by request" << endl; - exeunt(ex_spam); + badnews.push_back("by request"); } if (!date.length()) { - cerr << progid << " rejection: no date" << endl; - exeunt(ex_spam); // disallow mail with no date + badnews.push_back("no date"); + } + + if (mid_required && !message_id.length()) { + badnews.push_back("no message-id"); + } + + if (badnews.size()){ + cerr << progid << " " << join(", ", badnews) << endl; + if (error_exit){ + cerr << progid << " '" << from + << "' to '" << to + << "'" << endl; + exeunt(ex_spam); + } } string main_contype; - parse_content(content_type, main_contype, boundary); + if (content_type.length()) + parse_content(content_type, main_contype, boundary); +// some slightly-useful booleans: int currently_text = main_contype == "text"; int main_multipart = main_contype == "multipart"; // early-stage thinking has been done. // Now spew the rest of the message - cerr << "body begins: " << main_contype << " " << currently_text << " " << boundary << endl; + //xxxx cerr << "body begins: " << main_contype << " " << currently_text << " " << boundary << endl; int in_subheads(0); int textlines(0); @@ -345,7 +402,7 @@ int main(int _argc, const char** _argv){ msgsize += line.length()+1; if (msgsize > maxsize) { cerr << progid << " rejection: bigger than " << maxsize << endl; - exeunt(ex_spam); + maybe_exeunt(ex_spam, error_exit); } bigbuf.push_back(line); cout << line << endl; @@ -380,10 +437,10 @@ int main(int _argc, const char** _argv){ } } - if (1) cerr << "textlines: " << textlines << endl; + if (0) cerr << "textlines: " << textlines << endl; if (1 && !textlines) { cerr << progid << " rejection: no text" << endl; -// exeunt(ex_spam); +// maybe_exeunt(ex_spam, error_exit); } cerr << progid << " normal completion" << endl; exit(ex_good); -- cgit v1.2.3 From 9aa998757a8736cef02fd92050eacbb2a6fb5180 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 26 Jul 2012 10:57:13 -0700 Subject: patch to support IPv6 in tcpserver --- .gitignore | 1 + ucspi-tcp-0.88/FILES | 37 +++++++++ ucspi-tcp-0.88/Makefile | 184 +++++++++++++++++++++++++++++++++++------ ucspi-tcp-0.88/TARGETS | 28 +++++++ ucspi-tcp-0.88/dns.h | 63 ++++++++------ ucspi-tcp-0.88/dns_dfd.c | 11 +-- ucspi-tcp-0.88/dns_domain.c | 36 +++++--- ucspi-tcp-0.88/dns_dtda.c | 2 +- ucspi-tcp-0.88/dns_ip.c | 4 +- ucspi-tcp-0.88/dns_ipq.c | 6 +- ucspi-tcp-0.88/dns_name.c | 19 ++++- ucspi-tcp-0.88/dns_nd.c | 2 +- ucspi-tcp-0.88/dns_packet.c | 9 +- ucspi-tcp-0.88/dns_random.c | 3 +- ucspi-tcp-0.88/dns_rcip.c | 29 ++++--- ucspi-tcp-0.88/dns_rcrw.c | 5 +- ucspi-tcp-0.88/dns_resolve.c | 7 +- ucspi-tcp-0.88/dns_transmit.c | 71 ++++++++-------- ucspi-tcp-0.88/dns_txt.c | 4 +- ucspi-tcp-0.88/hier.c | 19 +++++ ucspi-tcp-0.88/ip4.h | 2 + ucspi-tcp-0.88/pathexec.h | 2 +- ucspi-tcp-0.88/pathexec_env.c | 3 +- ucspi-tcp-0.88/rblsmtpd.c | 46 +++++++++-- ucspi-tcp-0.88/remoteinfo.h | 1 + ucspi-tcp-0.88/rules.c | 2 +- ucspi-tcp-0.88/socket.h | 39 ++++++++- ucspi-tcp-0.88/socket_bind.c | 4 +- ucspi-tcp-0.88/socket_conn.c | 2 +- ucspi-tcp-0.88/str.h | 14 ++-- ucspi-tcp-0.88/str_chr.c | 4 +- ucspi-tcp-0.88/str_diff.c | 2 +- ucspi-tcp-0.88/str_len.c | 4 +- ucspi-tcp-0.88/str_start.c | 2 +- ucspi-tcp-0.88/stralloc.h | 12 +-- ucspi-tcp-0.88/stralloc_catb.c | 2 +- ucspi-tcp-0.88/stralloc_cats.c | 2 +- ucspi-tcp-0.88/stralloc_opyb.c | 2 +- ucspi-tcp-0.88/stralloc_opys.c | 2 +- ucspi-tcp-0.88/tcpclient.c | 73 ++++++++++------ ucspi-tcp-0.88/tcprules.c | 11 ++- ucspi-tcp-0.88/tcpserver.c | 112 +++++++++++++++++-------- ucspi-tcp-0.88/timeoutconn.h | 2 + 43 files changed, 644 insertions(+), 241 deletions(-) diff --git a/.gitignore b/.gitignore index ad1d359..e929027 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *#[0-9]* \#*# +ucspi-tcp-0.88-ipv6.patch has?????.h auto-gid auto-int diff --git a/ucspi-tcp-0.88/FILES b/ucspi-tcp-0.88/FILES index cfb38a5..142aed9 100644 --- a/ucspi-tcp-0.88/FILES +++ b/ucspi-tcp-0.88/FILES @@ -216,3 +216,40 @@ wait_pid.c warn-auto.sh warn-shsgr x86cpuid.c +dns_ip6.c +dns_ipq6.c +dns_nd6.c +dns_sortip6.c +fmt_xlong.c +ip6_fmt.c +ip6_scan.c +scan_0x.c +socket_accept6.c +socket_bind6.c +socket_conn6.c +socket_local6.c +socket_recv6.c +socket_remote6.c +socket_send6.c +socket_tcp6.c +timeoutconn6.c +tryip6.c +haveip6.h2 +haveip6.h1 +remoteinfo6.c +addcr.1 +argv0.1 +date@.1 +delcr.1 +finger@.1 +fixcr.1 +http@.1 +mconnect.1 +recordio.1 +tcp-environ.5 +tcpcat.1 +tcpclient.1 +tcprules.1 +tcprulescheck.1 +tcpserver.1 +who@.1 diff --git a/ucspi-tcp-0.88/Makefile b/ucspi-tcp-0.88/Makefile index a67b0cb..1b7c9dc 100644 --- a/ucspi-tcp-0.88/Makefile +++ b/ucspi-tcp-0.88/Makefile @@ -76,12 +76,14 @@ byte.a: \ makelib byte_chr.o byte_copy.o byte_cr.o byte_diff.o byte_rchr.o \ byte_zero.o case_diffb.o case_diffs.o fmt_ulong.o ip4_fmt.o \ ip4_scan.o scan_ulong.o str_chr.o str_diff.o str_len.o str_start.o \ -uint16_pack.o uint16_unpack.o uint32_pack.o uint32_unpack.o +uint16_pack.o uint16_unpack.o uint32_pack.o uint32_unpack.o \ +ip6_fmt.o scan_ip6.o scan_xlong.o fmt_xlong.o ./makelib byte.a byte_chr.o byte_copy.o byte_cr.o \ byte_diff.o byte_rchr.o byte_zero.o case_diffb.o \ case_diffs.o fmt_ulong.o ip4_fmt.o ip4_scan.o scan_ulong.o \ str_chr.o str_diff.o str_len.o str_start.o uint16_pack.o \ - uint16_unpack.o uint32_pack.o uint32_unpack.o + uint16_unpack.o uint32_pack.o uint32_unpack.o ip6_fmt.o \ + scan_ip6.o scan_xlong.o fmt_xlong.o byte_chr.o: \ compile byte_chr.c byte.h @@ -181,11 +183,13 @@ compile delcr.c buffer.h exit.h dns.a: \ makelib dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o dns_ipq.o \ dns_name.o dns_nd.o dns_packet.o dns_random.o dns_rcip.o dns_rcrw.o \ -dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o +dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o dns_ip6.o \ +dns_sortip6.o dns_nd6.o dns_ipq6.o ./makelib dns.a dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o \ dns_ipq.o dns_name.o dns_nd.o dns_packet.o dns_random.o \ dns_rcip.o dns_rcrw.o dns_resolve.o dns_sortip.o \ - dns_transmit.o dns_txt.o + dns_transmit.o dns_txt.o dns_ip6.o dns_sortip6.o dns_nd6.o \ + dns_ipq6.o dns_dfd.o: \ compile dns_dfd.c error.h alloc.h byte.h dns.h stralloc.h gen_alloc.h \ @@ -257,7 +261,7 @@ taia.h tai.h uint64.h taia.h dns_transmit.o: \ compile dns_transmit.c socket.h uint16.h alloc.h error.h byte.h \ readwrite.h uint16.h dns.h stralloc.h gen_alloc.h iopause.h taia.h \ -tai.h uint64.h taia.h +tai.h uint64.h taia.h uint32.h ./compile dns_transmit.c dns_txt.o: \ @@ -498,9 +502,15 @@ exit.h fmt.h iopause.h taia.h tai.h uint64.h pathexec.h remoteinfo.o: \ compile remoteinfo.c fmt.h buffer.h socket.h uint16.h error.h \ iopause.h taia.h tai.h uint64.h timeoutconn.h uint16.h remoteinfo.h \ -stralloc.h gen_alloc.h uint16.h +stralloc.h gen_alloc.h uint16.h uint32.h ./compile remoteinfo.c +remoteinfo6.o: \ +compile remoteinfo6.c fmt.h buffer.h socket.h uint16.h error.h \ +iopause.h taia.h tai.h uint64.h timeoutconn.h uint16.h remoteinfo.h \ +stralloc.h gen_alloc.h uint16.h uint32.h + ./compile remoteinfo6.c + rts: \ warn-auto.sh rts.sh conf-home cat warn-auto.sh rts.sh \ @@ -557,43 +567,43 @@ trylsock.c compile load rm -f trylsock.o trylsock socket_accept.o: \ -compile socket_accept.c byte.h socket.h uint16.h +compile socket_accept.c byte.h socket.h uint16.h uint32.h ./compile socket_accept.c socket_bind.o: \ -compile socket_bind.c byte.h socket.h uint16.h +compile socket_bind.c byte.h socket.h uint16.h uint32.h ./compile socket_bind.c socket_conn.o: \ -compile socket_conn.c readwrite.h byte.h socket.h uint16.h +compile socket_conn.c readwrite.h byte.h socket.h uint16.h uint32.h ./compile socket_conn.c socket_delay.o: \ -compile socket_delay.c socket.h uint16.h +compile socket_delay.c socket.h uint16.h uint32.h ./compile socket_delay.c socket_listen.o: \ -compile socket_listen.c socket.h uint16.h +compile socket_listen.c socket.h uint16.h uint32.h ./compile socket_listen.c socket_local.o: \ -compile socket_local.c byte.h socket.h uint16.h +compile socket_local.c byte.h socket.h uint16.h uint32.h ./compile socket_local.c socket_opts.o: \ -compile socket_opts.c socket.h uint16.h +compile socket_opts.c socket.h uint16.h uint32.h ./compile socket_opts.c socket_remote.o: \ -compile socket_remote.c byte.h socket.h uint16.h +compile socket_remote.c byte.h socket.h uint16.h uint32.h ./compile socket_remote.c socket_tcp.o: \ -compile socket_tcp.c ndelay.h socket.h uint16.h +compile socket_tcp.c ndelay.h socket.h uint16.h uint32.h ./compile socket_tcp.c socket_udp.o: \ -compile socket_udp.c ndelay.h socket.h uint16.h +compile socket_udp.c ndelay.h socket.h uint16.h uint32.h ./compile socket_udp.c str_chr.o: \ @@ -710,9 +720,9 @@ warn-auto.sh tcpcat.sh conf-home chmod 755 tcpcat tcpclient: \ -load tcpclient.o remoteinfo.o timeoutconn.o dns.a time.a unix.a \ -byte.a socket.lib - ./load tcpclient remoteinfo.o timeoutconn.o dns.a time.a \ +load tcpclient.o remoteinfo6.o dns.a time.a unix.a \ +byte.a socket.lib byte.h timeoutconn6.o + ./load tcpclient remoteinfo6.o timeoutconn6.o dns.a time.a \ unix.a byte.a `cat socket.lib` tcpclient.o: \ @@ -720,7 +730,7 @@ compile tcpclient.c sig.h exit.h sgetopt.h subgetopt.h uint16.h fmt.h \ scan.h str.h ip4.h uint16.h socket.h uint16.h fd.h stralloc.h \ gen_alloc.h buffer.h error.h strerr.h pathexec.h timeoutconn.h \ uint16.h remoteinfo.h stralloc.h uint16.h dns.h stralloc.h iopause.h \ -taia.h tai.h uint64.h taia.h +taia.h tai.h uint64.h taia.h uint32.h ./compile tcpclient.c tcprules: \ @@ -742,9 +752,9 @@ stralloc.h gen_alloc.h ./compile tcprulescheck.c tcpserver: \ -load tcpserver.o rules.o remoteinfo.o timeoutconn.o cdb.a dns.a \ +load tcpserver.o rules.o remoteinfo6.o timeoutconn6.o cdb.a dns.a \ time.a unix.a byte.a socket.lib - ./load tcpserver rules.o remoteinfo.o timeoutconn.o cdb.a \ + ./load tcpserver rules.o remoteinfo6.o timeoutconn6.o cdb.a \ dns.a time.a unix.a byte.a `cat socket.lib` tcpserver.o: \ @@ -753,7 +763,7 @@ exit.h env.h prot.h open.h wait.h readwrite.h stralloc.h gen_alloc.h \ alloc.h buffer.h error.h strerr.h sgetopt.h subgetopt.h pathexec.h \ socket.h uint16.h ndelay.h remoteinfo.h stralloc.h uint16.h rules.h \ stralloc.h sig.h dns.h stralloc.h iopause.h taia.h tai.h uint64.h \ -taia.h +taia.h uint32.h ./compile tcpserver.c time.a: \ @@ -765,9 +775,14 @@ taia_less.o taia_now.o taia_pack.o taia_sub.o taia_uint.o timeoutconn.o: \ compile timeoutconn.c ndelay.h socket.h uint16.h iopause.h taia.h \ -tai.h uint64.h error.h timeoutconn.h uint16.h +tai.h uint64.h error.h timeoutconn.h uint16.h uint32.h ./compile timeoutconn.c +timeoutconn6.o: \ +compile timeoutconn6.c ndelay.h socket.h uint16.h iopause.h taia.h \ +tai.h uint64.h error.h timeoutconn.h uint16.h uint32.h + ./compile timeoutconn6.c + uint16_pack.o: \ compile uint16_pack.c uint16.h ./compile uint16_pack.c @@ -806,7 +821,12 @@ socket_conn.o socket_delay.o socket_listen.o socket_local.o \ socket_opts.o socket_remote.o socket_tcp.o socket_udp.o \ stralloc_cat.o stralloc_catb.o stralloc_cats.o stralloc_copy.o \ stralloc_eady.o stralloc_opyb.o stralloc_opys.o stralloc_pend.o \ -strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.o +strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.o \ +socket_conn6.o socket_bind6.o socket_accept6.o socket_recv6.o \ +socket_send6.o socket_local6.o socket_remote6.o socket_tcp6.o \ +socket_getifname.o socket_getifidx.o socket_v4mappedprefix.o \ +socket_ip4loopback.o socket_v6any.o socket_v6loopback.o \ +socket_udp6.o ./makelib unix.a alloc.o alloc_re.o buffer.o buffer_0.o \ buffer_1.o buffer_2.o buffer_copy.o buffer_get.o \ buffer_put.o env.o error.o error_str.o fd_copy.o fd_move.o \ @@ -819,7 +839,12 @@ strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.o socket_udp.o stralloc_cat.o stralloc_catb.o stralloc_cats.o \ stralloc_copy.o stralloc_eady.o stralloc_opyb.o \ stralloc_opys.o stralloc_pend.o strerr_die.o strerr_sys.o \ - subgetopt.o wait_nohang.o wait_pid.o + subgetopt.o wait_nohang.o wait_pid.o socket_conn6.o \ + socket_bind6.o socket_accept6.o socket_recv6.o socket_send6.o \ + socket_local6.o socket_remote6.o socket_tcp6.o \ + socket_getifname.o socket_getifidx.o socket_v4mappedprefix.o \ + socket_ip4loopback.o socket_v6any.o socket_v6loopback.o \ + socket_udp6.o wait_nohang.o: \ compile wait_nohang.c haswaitp.h @@ -835,3 +860,110 @@ warn-auto.sh who@.sh conf-home | sed s}HOME}"`head -1 conf-home`"}g \ > who@ chmod 755 who@ + +socket_conn6.o: \ +compile socket_conn6.c socket.h uint16.h haveip6.h error.h ip6.h \ +uint32.h + ./compile socket_conn6.c + +socket_bind6.o: \ +compile socket_bind6.c socket.h uint16.h haveip6.h error.h ip6.h \ +uint32.h + ./compile socket_bind6.c + +socket_accept6.o: \ +compile socket_accept6.c socket.h uint16.h haveip6.h error.h ip6.h \ +uint32.h + ./compile socket_accept6.c + +socket_recv6.o: \ +compile socket_recv6.c socket.h uint16.h haveip6.h error.h ip6.h \ +uint32.h + ./compile socket_recv6.c + +socket_send6.o: \ +compile socket_send6.c socket.h uint16.h haveip6.h error.h uint32.h + ./compile socket_send6.c + +socket_local6.o: \ +compile socket_local6.c socket.h uint16.h haveip6.h error.h uint32.h + ./compile socket_local6.c + +socket_remote6.o: \ +compile socket_remote6.c socket.h uint16.h haveip6.h error.h uint32.h + ./compile socket_remote6.c + +dns_sortip6.o: \ +compile dns_sortip6.c byte.h dns.h stralloc.h gen_alloc.h iopause.h \ +taia.h tai.h uint64.h taia.h + ./compile dns_sortip6.c + +dns_nd6.o: \ +compile dns_nd6.c byte.h fmt.h dns.h stralloc.h gen_alloc.h iopause.h \ +taia.h tai.h uint64.h taia.h + ./compile dns_nd6.c + +dns_ipq6.o: \ +compile dns_ipq6.c stralloc.h gen_alloc.h case.h byte.h str.h dns.h \ +stralloc.h iopause.h taia.h tai.h uint64.h taia.h ip6.h + ./compile dns_ipq6.c + +dns_ip6.o: \ +compile dns_ip6.c stralloc.h gen_alloc.h uint16.h byte.h dns.h \ +stralloc.h iopause.h taia.h tai.h uint64.h taia.h + ./compile dns_ip6.c + +fmt_xlong.o: \ +compile fmt_xlong.c scan.h + ./compile fmt_xlong.c + +scan_xlong.o: \ +compile scan_xlong.c scan.h + ./compile scan_xlong.c + +ip6_fmt.o: \ +compile ip6_fmt.c fmt.h ip6.h + ./compile ip6_fmt.c + +scan_ip6.o: \ +compile scan_ip6.c scan.h ip6.h + ./compile scan_ip6.c + +socket_tcp6.o: \ +compile socket_tcp6.c ndelay.h socket.h uint16.h haveip6.h uint32.h + ./compile socket_tcp6.c + +socket_udp6.o: \ +compile socket_udp6.c ndelay.h socket.h uint16.h haveip6.h uint32.h + ./compile socket_udp6.c + +haveip6.h: \ +tryip6.c choose compile haveip6.h1 haveip6.h2 + ./choose c tryip6 haveip6.h1 haveip6.h2 > haveip6.h + +socket_getifname.o: \ +compile socket_getifname.c socket.h uint16.h uint32.h + ./compile socket_getifname.c + +socket_getifidx.o: \ +compile socket_getifidx.c socket.h uint16.h uint32.h + ./compile socket_getifidx.c + +socket_ip4loopback.o: \ +compile socket_ip4loopback.c + ./compile socket_ip4loopback.c + +socket_v4mappedprefix.o: \ +compile socket_v4mappedprefix.c + ./compile socket_v4mappedprefix.c + +socket_v6any.o: \ +compile socket_v6any.c + ./compile socket_v6any.c + +socket_v6loopback.o: \ +compile socket_v6loopback.c + ./compile socket_v6loopback.c + +clean: + rm -f `cat TARGETS` diff --git a/ucspi-tcp-0.88/TARGETS b/ucspi-tcp-0.88/TARGETS index 4d1f2a0..0385f96 100644 --- a/ucspi-tcp-0.88/TARGETS +++ b/ucspi-tcp-0.88/TARGETS @@ -169,3 +169,31 @@ instcheck it setup check +dns_ip6.o +dns_ipq6.o +dns_nd6.o +dns_sortip6.o +fmt_xlong.o +ip6_fmt.o +ip6_scan.o +scan_0x.o +socket_accept6.o +socket_bind6.o +socket_conn6.o +socket_local6.o +socket_recv6.o +socket_remote6.o +socket_send6.o +socket_tcp6.o +timeoutconn6.o +haveip6.h +remoteinfo6.o +socket_getifidx.o +socket_getifname.o +scan_ip6.o +scan_xlong.o +socket_ip4loopback.o +socket_udp6.o +socket_v4mappedprefix.o +socket_v6any.o +socket_v6loopback.o diff --git a/ucspi-tcp-0.88/dns.h b/ucspi-tcp-0.88/dns.h index 0948b1a..f06c5a8 100644 --- a/ucspi-tcp-0.88/dns.h +++ b/ucspi-tcp-0.88/dns.h @@ -34,51 +34,60 @@ struct dns_transmit { unsigned int curserver; struct taia deadline; unsigned int pos; - char *servers; - char localip[4]; + const char *servers; + char localip[16]; + unsigned int scope_id; char qtype[2]; } ; -extern void dns_random_init(char *); +extern void dns_random_init(const char *); extern unsigned int dns_random(unsigned int); extern void dns_sortip(char *,unsigned int); +extern void dns_sortip6(char *,unsigned int); extern void dns_domain_free(char **); -extern int dns_domain_copy(char **,char *); -extern unsigned int dns_domain_length(char *); -extern int dns_domain_equal(char *,char *); -extern char *dns_domain_suffix(char *,char *); -extern int dns_domain_fromdot(char **,char *,unsigned int); -extern int dns_domain_todot_cat(stralloc *,char *); +extern int dns_domain_copy(char **,const char *); +extern unsigned int dns_domain_length(const char *); +extern int dns_domain_equal(const char *,const char *); +extern int dns_domain_suffix(const char *,const char *); +extern unsigned int dns_domain_suffixpos(const char *,const char *); +extern int dns_domain_fromdot(char **,const char *,unsigned int); +extern int dns_domain_todot_cat(stralloc *,const char *); -extern unsigned int dns_packet_copy(char *,unsigned int,unsigned int,char *,unsigned int); -extern unsigned int dns_packet_getname(char *,unsigned int,unsigned int,char **); -extern unsigned int dns_packet_skipname(char *,unsigned int,unsigned int); -extern int dns_packet_nameequal(char *,unsigned int,unsigned int,char *,unsigned int,unsigned int); +extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int); +extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **); +extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int); -extern int dns_transmit_start(struct dns_transmit *,char *,int,char *,char *,char *); +extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *); extern void dns_transmit_free(struct dns_transmit *); extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *); -extern int dns_transmit_get(struct dns_transmit *,iopause_fd *,struct taia *); +extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *); extern int dns_resolvconfip(char *); -extern int dns_resolve(char *,char *); +extern int dns_resolve(const char *,const char *); extern struct dns_transmit dns_resolve_tx; -extern int dns_ip4_packet(stralloc *,char *,unsigned int); -extern int dns_ip4(stralloc *,stralloc *); -extern int dns_name_packet(stralloc *,char *,unsigned int); -extern void dns_name4_domain(char *,char *); +extern int dns_ip4_packet(stralloc *,const char *,unsigned int); +extern int dns_ip4(stralloc *,const stralloc *); +extern int dns_ip6_packet(stralloc *,const char *,unsigned int); +extern int dns_ip6(stralloc *,stralloc *); +extern int dns_name_packet(stralloc *,const char *,unsigned int); +extern void dns_name4_domain(char *,const char *); #define DNS_NAME4_DOMAIN 31 -extern int dns_name4(stralloc *,char *); -extern int dns_txt_packet(stralloc *,char *,unsigned int); -extern int dns_txt(stralloc *,stralloc *); -extern int dns_mx_packet(stralloc *,char *,unsigned int); -extern int dns_mx(stralloc *,stralloc *); +extern int dns_name4(stralloc *,const char *); +extern int dns_txt_packet(stralloc *,const char *,unsigned int); +extern int dns_txt(stralloc *,const stralloc *); +extern int dns_mx_packet(stralloc *,const char *,unsigned int); +extern int dns_mx(stralloc *,const stralloc *); extern int dns_resolvconfrewrite(stralloc *); -extern int dns_ip4_qualify_rules(stralloc *,stralloc *,stralloc *,stralloc *); -extern int dns_ip4_qualify(stralloc *,stralloc *,stralloc *); +extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); +extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *); +extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); +extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *); + +extern int dns_name6_domain(char *,char *); +#define DNS_NAME6_DOMAIN (4*16+11) #endif diff --git a/ucspi-tcp-0.88/dns_dfd.c b/ucspi-tcp-0.88/dns_dfd.c index 14a29d8..c924718 100644 --- a/ucspi-tcp-0.88/dns_dfd.c +++ b/ucspi-tcp-0.88/dns_dfd.c @@ -1,9 +1,10 @@ -#include "error.h" -#include "alloc.h" +#include +#include #include "byte.h" #include "dns.h" +#include "error.h" -int dns_domain_fromdot(char **out,char *buf,unsigned int n) +int dns_domain_fromdot(char **out,const char *buf,unsigned int n) { char label[63]; unsigned int labellen = 0; /* <= sizeof label */ @@ -59,11 +60,11 @@ int dns_domain_fromdot(char **out,char *buf,unsigned int n) if (namelen + 1 > sizeof name) return 0; name[namelen++] = 0; - x = alloc(namelen); + x = malloc(namelen); if (!x) return 0; byte_copy(x,namelen,name); - if (*out) alloc_free(*out); + if (*out) free(*out); *out = x; return 1; } diff --git a/ucspi-tcp-0.88/dns_domain.c b/ucspi-tcp-0.88/dns_domain.c index f898485..80ac5ea 100644 --- a/ucspi-tcp-0.88/dns_domain.c +++ b/ucspi-tcp-0.88/dns_domain.c @@ -1,16 +1,15 @@ -#include "error.h" -#include "alloc.h" +#include #include "case.h" #include "byte.h" #include "dns.h" -unsigned int dns_domain_length(char *dn) +unsigned int dns_domain_length(const char *dn) { - char *x; + const char *x; unsigned char c; x = dn; - while (c = *x++) + while ((c = *x++)) x += (unsigned int) c; return x - dn; } @@ -18,26 +17,26 @@ unsigned int dns_domain_length(char *dn) void dns_domain_free(char **out) { if (*out) { - alloc_free(*out); + free(*out); *out = 0; } } -int dns_domain_copy(char **out,char *in) +int dns_domain_copy(char **out,const char *in) { unsigned int len; char *x; len = dns_domain_length(in); - x = alloc(len); + x = malloc(len); if (!x) return 0; byte_copy(x,len,in); - if (*out) alloc_free(*out); + if (*out) free(*out); *out = x; return 1; } -int dns_domain_equal(char *dn1,char *dn2) +int dns_domain_equal(const char *dn1,const char *dn2) { unsigned int len; @@ -48,12 +47,25 @@ int dns_domain_equal(char *dn1,char *dn2) return 1; } -char *dns_domain_suffix(char *big,char *little) +int dns_domain_suffix(const char *big,const char *little) +{ + unsigned char c; + + for (;;) { + if (dns_domain_equal(big,little)) return 1; + c = *big++; + if (!c) return 0; + big += c; + } +} + +unsigned int dns_domain_suffixpos(const char *big,const char *little) { + const char *orig = big; unsigned char c; for (;;) { - if (dns_domain_equal(big,little)) return big; + if (dns_domain_equal(big,little)) return big - orig; c = *big++; if (!c) return 0; big += c; diff --git a/ucspi-tcp-0.88/dns_dtda.c b/ucspi-tcp-0.88/dns_dtda.c index 00b41a1..ba1db4f 100644 --- a/ucspi-tcp-0.88/dns_dtda.c +++ b/ucspi-tcp-0.88/dns_dtda.c @@ -1,7 +1,7 @@ #include "stralloc.h" #include "dns.h" -int dns_domain_todot_cat(stralloc *out,char *d) +int dns_domain_todot_cat(stralloc *out,const char *d) { char ch; char ch2; diff --git a/ucspi-tcp-0.88/dns_ip.c b/ucspi-tcp-0.88/dns_ip.c index fb0526c..e7c3a9a 100644 --- a/ucspi-tcp-0.88/dns_ip.c +++ b/ucspi-tcp-0.88/dns_ip.c @@ -3,7 +3,7 @@ #include "byte.h" #include "dns.h" -int dns_ip4_packet(stralloc *out,char *buf,unsigned int len) +int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len) { unsigned int pos; char header[12]; @@ -36,7 +36,7 @@ int dns_ip4_packet(stralloc *out,char *buf,unsigned int len) static char *q = 0; -int dns_ip4(stralloc *out,stralloc *fqdn) +int dns_ip4(stralloc *out,const stralloc *fqdn) { unsigned int i; char code; diff --git a/ucspi-tcp-0.88/dns_ipq.c b/ucspi-tcp-0.88/dns_ipq.c index 8181ab7..5b65e23 100644 --- a/ucspi-tcp-0.88/dns_ipq.c +++ b/ucspi-tcp-0.88/dns_ipq.c @@ -4,7 +4,7 @@ #include "str.h" #include "dns.h" -static int doit(stralloc *work,char *rule) +static int doit(stralloc *work,const char *rule) { char ch; unsigned int colon; @@ -30,7 +30,7 @@ static int doit(stralloc *work,char *rule) return stralloc_cats(work,rule + colon + 1); } -int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,stralloc *in,stralloc *rules) +int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules) { unsigned int i; unsigned int j; @@ -63,7 +63,7 @@ int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,stralloc *in,stralloc *ru } } -int dns_ip4_qualify(stralloc *out,stralloc *fqdn,stralloc *in) +int dns_ip4_qualify(stralloc *out,stralloc *fqdn,const stralloc *in) { static stralloc rules; if (dns_resolvconfrewrite(&rules) == -1) return -1; diff --git a/ucspi-tcp-0.88/dns_name.c b/ucspi-tcp-0.88/dns_name.c index dcb10c7..1f03186 100644 --- a/ucspi-tcp-0.88/dns_name.c +++ b/ucspi-tcp-0.88/dns_name.c @@ -2,10 +2,11 @@ #include "uint16.h" #include "byte.h" #include "dns.h" +#include "ip6.h" static char *q = 0; -int dns_name_packet(stralloc *out,char *buf,unsigned int len) +int dns_name_packet(stralloc *out,const char *buf,unsigned int len) { unsigned int pos; char header[12]; @@ -35,7 +36,7 @@ int dns_name_packet(stralloc *out,char *buf,unsigned int len) return 0; } -int dns_name4(stralloc *out,char ip[4]) +int dns_name4(stralloc *out,const char ip[4]) { char name[DNS_NAME4_DOMAIN]; @@ -46,3 +47,17 @@ int dns_name4(stralloc *out,char ip[4]) dns_domain_free(&q); return 0; } + +int dns_name6(stralloc *out,char ip[16]) +{ + char name[DNS_NAME6_DOMAIN]; + + if (ip6_isv4mapped(ip)) + return dns_name4(out,ip+12); + dns_name6_domain(name,ip); + if (dns_resolve(name,DNS_T_PTR) == -1) return -1; + if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + return 0; +} diff --git a/ucspi-tcp-0.88/dns_nd.c b/ucspi-tcp-0.88/dns_nd.c index 279d74d..aa54e5d 100644 --- a/ucspi-tcp-0.88/dns_nd.c +++ b/ucspi-tcp-0.88/dns_nd.c @@ -2,7 +2,7 @@ #include "fmt.h" #include "dns.h" -void dns_name4_domain(char name[DNS_NAME4_DOMAIN],char ip[4]) +void dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4]) { unsigned int namelen; unsigned int i; diff --git a/ucspi-tcp-0.88/dns_packet.c b/ucspi-tcp-0.88/dns_packet.c index 04a2cc8..72cfb35 100644 --- a/ucspi-tcp-0.88/dns_packet.c +++ b/ucspi-tcp-0.88/dns_packet.c @@ -2,10 +2,11 @@ DNS should have used LZ77 instead of its own sophomoric compression algorithm. */ -#include "error.h" +#include #include "dns.h" +#include "error.h" -unsigned int dns_packet_copy(char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen) +unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen) { while (outlen) { if (pos >= len) { errno = error_proto; return 0; } @@ -15,7 +16,7 @@ unsigned int dns_packet_copy(char *buf,unsigned int len,unsigned int pos,char *o return pos; } -unsigned int dns_packet_skipname(char *buf,unsigned int len,unsigned int pos) +unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos) { unsigned char ch; @@ -32,7 +33,7 @@ unsigned int dns_packet_skipname(char *buf,unsigned int len,unsigned int pos) return 0; } -unsigned int dns_packet_getname(char *buf,unsigned int len,unsigned int pos,char **d) +unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d) { unsigned int loop = 0; unsigned int state = 0; diff --git a/ucspi-tcp-0.88/dns_random.c b/ucspi-tcp-0.88/dns_random.c index b9892b4..2158ed4 100644 --- a/ucspi-tcp-0.88/dns_random.c +++ b/ucspi-tcp-0.88/dns_random.c @@ -1,3 +1,4 @@ +#include #include "dns.h" #include "taia.h" #include "uint32.h" @@ -29,7 +30,7 @@ static void surf(void) } } -void dns_random_init(char data[128]) +void dns_random_init(const char data[128]) { int i; struct taia t; diff --git a/ucspi-tcp-0.88/dns_rcip.c b/ucspi-tcp-0.88/dns_rcip.c index 2356c8b..794f6be 100644 --- a/ucspi-tcp-0.88/dns_rcip.c +++ b/ucspi-tcp-0.88/dns_rcip.c @@ -2,12 +2,13 @@ #include "openreadclose.h" #include "byte.h" #include "ip4.h" -#include "env.h" +#include "ip6.h" #include "dns.h" +#include "env.h" static stralloc data = {0}; -static int init(char ip[64]) +static int init(char ip[256]) { int i; int j; @@ -16,15 +17,16 @@ static int init(char ip[64]) x = env_get("DNSCACHEIP"); if (x) - while (iplen <= 60) + while (iplen <= 60) { if (*x == '.') ++x; else { - i = ip4_scan(x,ip + iplen); + i = scan_ip6(x,ip + iplen); if (!i) break; x += i; - iplen += 4; + iplen += 16; } + } if (!iplen) { i = openreadclose("/etc/resolv.conf",&data,64); @@ -39,8 +41,9 @@ static int init(char ip[64]) while ((data.s[i] == ' ') || (data.s[i] == '\t')) ++i; if (iplen <= 60) - if (ip4_scan(data.s + i,ip + iplen)) - iplen += 4; + if (scan_ip6(data.s + i,ip + iplen)) { + iplen += 16; + } } i = j + 1; } @@ -48,19 +51,19 @@ static int init(char ip[64]) } if (!iplen) { - byte_copy(ip,4,"\177\0\0\1"); - iplen = 4; + byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"); + iplen = 16; } - byte_zero(ip + iplen,64 - iplen); + byte_zero(ip + iplen,256 - iplen); return 0; } static int ok = 0; static unsigned int uses; static struct taia deadline; -static char ip[64]; /* defined if ok */ +static char ip[256]; /* defined if ok */ -int dns_resolvconfip(char s[64]) +int dns_resolvconfip(char s[256]) { struct taia now; @@ -77,6 +80,6 @@ int dns_resolvconfip(char s[64]) } --uses; - byte_copy(s,64,ip); + byte_copy(s,256,ip); return 0; } diff --git a/ucspi-tcp-0.88/dns_rcrw.c b/ucspi-tcp-0.88/dns_rcrw.c index 6f215ac..b0c8e6d 100644 --- a/ucspi-tcp-0.88/dns_rcrw.c +++ b/ucspi-tcp-0.88/dns_rcrw.c @@ -1,16 +1,17 @@ +#include #include "taia.h" -#include "env.h" #include "byte.h" #include "str.h" #include "openreadclose.h" #include "dns.h" +#include "env.h" static stralloc data = {0}; static int init(stralloc *rules) { char host[256]; - char *x; + const char *x; int i; int j; int k; diff --git a/ucspi-tcp-0.88/dns_resolve.c b/ucspi-tcp-0.88/dns_resolve.c index 3365c00..82b5bbb 100644 --- a/ucspi-tcp-0.88/dns_resolve.c +++ b/ucspi-tcp-0.88/dns_resolve.c @@ -2,19 +2,20 @@ #include "taia.h" #include "byte.h" #include "dns.h" +#include "ip6.h" struct dns_transmit dns_resolve_tx = {0}; -int dns_resolve(char *q,char qtype[2]) +int dns_resolve(const char *q,const char qtype[2]) { struct taia stamp; struct taia deadline; - char servers[64]; + char servers[256]; iopause_fd x[1]; int r; if (dns_resolvconfip(servers) == -1) return -1; - if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,"\0\0\0\0") == -1) return -1; + if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,V6any) == -1) return -1; for (;;) { taia_now(&stamp); diff --git a/ucspi-tcp-0.88/dns_transmit.c b/ucspi-tcp-0.88/dns_transmit.c index df12826..9511511 100644 --- a/ucspi-tcp-0.88/dns_transmit.c +++ b/ucspi-tcp-0.88/dns_transmit.c @@ -1,12 +1,15 @@ +#include +#include +#include +#include #include "socket.h" -#include "alloc.h" -#include "error.h" +#include #include "byte.h" -#include "readwrite.h" #include "uint16.h" #include "dns.h" +#include "ip6.h" -static int serverwantstcp(char *buf,unsigned int len) +static int serverwantstcp(const char *buf,unsigned int len) { char out[12]; @@ -15,7 +18,7 @@ static int serverwantstcp(char *buf,unsigned int len) return 0; } -static int serverfailed(char *buf,unsigned int len) +static int serverfailed(const char *buf,unsigned int len) { char out[12]; unsigned int rcode; @@ -23,11 +26,11 @@ static int serverfailed(char *buf,unsigned int len) if (!dns_packet_copy(buf,len,0,out,12)) return 1; rcode = out[3]; rcode &= 15; - if (rcode && (rcode != 3)) { errno = error_again; return 1; } + if (rcode && (rcode != 3)) { errno = EAGAIN; return 1; } return 0; } -static int irrelevant(struct dns_transmit *d,char *buf,unsigned int len) +static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len) { char out[12]; char *dn; @@ -40,8 +43,8 @@ static int irrelevant(struct dns_transmit *d,char *buf,unsigned int len) dn = 0; pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1; - if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; } - alloc_free(dn); + if (!dns_domain_equal(dn,d->query + 14)) { free(dn); return 1; } + free(dn); pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1; if (byte_diff(out,2,d->qtype)) return 1; @@ -53,14 +56,14 @@ static int irrelevant(struct dns_transmit *d,char *buf,unsigned int len) static void packetfree(struct dns_transmit *d) { if (!d->packet) return; - alloc_free(d->packet); + free(d->packet); d->packet = 0; } static void queryfree(struct dns_transmit *d) { if (!d->query) return; - alloc_free(d->query); + free(d->query); d->query = 0; } @@ -83,9 +86,9 @@ static int randombind(struct dns_transmit *d) int j; for (j = 0;j < 10;++j) - if (socket_bind4(d->s1 - 1,d->localip,1025 + dns_random(64510)) == 0) + if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0) return 0; - if (socket_bind4(d->s1 - 1,d->localip,0) == 0) + if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0) return 0; return -1; } @@ -94,22 +97,22 @@ static const int timeouts[4] = { 1, 3, 11, 45 }; static int thisudp(struct dns_transmit *d) { - char *ip; + const char *ip; socketfree(d); while (d->udploop < 4) { for (;d->curserver < 16;++d->curserver) { - ip = d->servers + 4 * d->curserver; - if (byte_diff(ip,4,"\0\0\0\0")) { + ip = d->servers + 16 * d->curserver; + if (byte_diff(ip,16,V6any)) { d->query[2] = dns_random(256); d->query[3] = dns_random(256); - d->s1 = 1 + socket_udp(); + d->s1 = 1 + socket_udp6(); if (!d->s1) { dns_transmit_free(d); return -1; } if (randombind(d) == -1) { dns_transmit_free(d); return -1; } - if (socket_connect4(d->s1 - 1,ip,53) == 0) + if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) { struct taia now; taia_now(&now); @@ -145,29 +148,29 @@ static int nextudp(struct dns_transmit *d) static int thistcp(struct dns_transmit *d) { struct taia now; - char *ip; + const char *ip; socketfree(d); packetfree(d); for (;d->curserver < 16;++d->curserver) { - ip = d->servers + 4 * d->curserver; - if (byte_diff(ip,4,"\0\0\0\0")) { + ip = d->servers + 16 * d->curserver; + if (byte_diff(ip,16,V6any)) { d->query[2] = dns_random(256); d->query[3] = dns_random(256); - d->s1 = 1 + socket_tcp(); + d->s1 = 1 + socket_tcp6(); if (!d->s1) { dns_transmit_free(d); return -1; } if (randombind(d) == -1) { dns_transmit_free(d); return -1; } taia_now(&now); taia_uint(&d->deadline,10); taia_add(&d->deadline,&d->deadline,&now); - if (socket_connect4(d->s1 - 1,ip,53) == 0) { + if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) { d->tcpstate = 2; return 0; } - if ((errno == error_inprogress) || (errno == error_wouldblock)) { + if ((errno == EINPROGRESS) || (errno == EWOULDBLOCK)) { d->tcpstate = 1; return 0; } @@ -191,16 +194,16 @@ static int nexttcp(struct dns_transmit *d) return thistcp(d); } -int dns_transmit_start(struct dns_transmit *d,char servers[64],int flagrecursive,char *q,char qtype[2],char localip[4]) +int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagrecursive,const char *q,const char qtype[2],const char localip[16]) { unsigned int len; dns_transmit_free(d); - errno = error_io; + errno = EIO; len = dns_domain_length(q); d->querylen = len + 18; - d->query = alloc(d->querylen); + d->query = malloc(d->querylen); if (!d->query) return -1; uint16_pack_big(d->query,len + 16); @@ -211,7 +214,7 @@ int dns_transmit_start(struct dns_transmit *d,char servers[64],int flagrecursive byte_copy(d->qtype,2,qtype); d->servers = servers; - byte_copy(d->localip,4,localip); + byte_copy(d->localip,16,localip); d->udploop = flagrecursive ? 1 : 0; @@ -236,19 +239,19 @@ void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline) *deadline = d->deadline; } -int dns_transmit_get(struct dns_transmit *d,iopause_fd *x,struct taia *when) +int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when) { char udpbuf[513]; unsigned char ch; int r; int fd; - errno = error_io; + errno = EIO; fd = d->s1 - 1; if (!x->revents) { if (taia_less(when,&d->deadline)) return 0; - errno = error_timeout; + errno = ETIMEDOUT; if (d->tcpstate == 0) return nextudp(d); return nexttcp(d); } @@ -260,7 +263,7 @@ have sent query to curserver on UDP socket s */ r = recv(fd,udpbuf,sizeof udpbuf,0); if (r <= 0) { - if (d->udploop == 2) return 0; + if (errno == ECONNREFUSED) if (d->udploop == 2) return 0; return nextudp(d); } if (r + 1 > sizeof udpbuf) return 0; @@ -274,7 +277,7 @@ have sent query to curserver on UDP socket s socketfree(d); d->packetlen = r; - d->packet = alloc(d->packetlen); + d->packet = malloc(d->packetlen); if (!d->packet) { dns_transmit_free(d); return -1; } byte_copy(d->packet,d->packetlen,udpbuf); queryfree(d); @@ -334,7 +337,7 @@ have received one byte of packet length into packetlen d->packetlen += ch; d->tcpstate = 5; d->pos = 0; - d->packet = alloc(d->packetlen); + d->packet = malloc(d->packetlen); if (!d->packet) { dns_transmit_free(d); return -1; } return 0; } diff --git a/ucspi-tcp-0.88/dns_txt.c b/ucspi-tcp-0.88/dns_txt.c index 263b641..44deafe 100644 --- a/ucspi-tcp-0.88/dns_txt.c +++ b/ucspi-tcp-0.88/dns_txt.c @@ -3,7 +3,7 @@ #include "byte.h" #include "dns.h" -int dns_txt_packet(stralloc *out,char *buf,unsigned int len) +int dns_txt_packet(stralloc *out,const char *buf,unsigned int len) { unsigned int pos; char header[12]; @@ -48,7 +48,7 @@ int dns_txt_packet(stralloc *out,char *buf,unsigned int len) static char *q = 0; -int dns_txt(stralloc *out,stralloc *fqdn) +int dns_txt(stralloc *out,const stralloc *fqdn) { if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1; if (dns_resolve(q,DNS_T_TXT) == -1) return -1; diff --git a/ucspi-tcp-0.88/hier.c b/ucspi-tcp-0.88/hier.c index 5663ada..546cc6d 100644 --- a/ucspi-tcp-0.88/hier.c +++ b/ucspi-tcp-0.88/hier.c @@ -4,6 +4,9 @@ void hier() { h(auto_home,-1,-1,02755); d(auto_home,"bin",-1,-1,02755); + d(auto_home,"man",-1,-1,02755); + d(auto_home,"man/man1",-1,-1,02755); + d(auto_home,"man/man5",-1,-1,02755); c(auto_home,"bin","tcpserver",-1,-1,0755); c(auto_home,"bin","tcprules",-1,-1,0755); @@ -22,4 +25,20 @@ void hier() c(auto_home,"bin","delcr",-1,-1,0755); c(auto_home,"bin","fixcrio",-1,-1,0755); c(auto_home,"bin","rblsmtpd",-1,-1,0755); + + c(auto_home,"man/man1","tcpclient.1",-1,-1,0644); + c(auto_home,"man/man1","tcpserver.1",-1,-1,0644); + c(auto_home,"man/man1","tcprules.1",-1,-1,0644); + c(auto_home,"man/man1","tcprulescheck.1",-1,-1,0644); + c(auto_home,"man/man1","fixcr.1",-1,-1,0644); + c(auto_home,"man/man1","addcr.1",-1,-1,0644); + c(auto_home,"man/man1","delcr.1",-1,-1,0644); + c(auto_home,"man/man1","who@.1",-1,-1,0644); + c(auto_home,"man/man1","date@.1",-1,-1,0644); + c(auto_home,"man/man1","finger@.1",-1,-1,0644); + c(auto_home,"man/man1","http@.1",-1,-1,0644); + c(auto_home,"man/man1","mconnect.1",-1,-1,0644); + c(auto_home,"man/man1","argv0.1",-1,-1,0644); + c(auto_home,"man/man1","recordio.1",-1,-1,0644); + c(auto_home,"man/man5","tcp-environ.5",-1,-1,0644); } diff --git a/ucspi-tcp-0.88/ip4.h b/ucspi-tcp-0.88/ip4.h index 64a7c1e..b906557 100644 --- a/ucspi-tcp-0.88/ip4.h +++ b/ucspi-tcp-0.88/ip4.h @@ -6,4 +6,6 @@ extern unsigned int ip4_fmt(char *,char *); #define IP4_FMT 20 +extern const char ip4loopback[4]; /* = {127,0,0,1}; */ + #endif diff --git a/ucspi-tcp-0.88/pathexec.h b/ucspi-tcp-0.88/pathexec.h index 6fcbb89..bef93b4 100644 --- a/ucspi-tcp-0.88/pathexec.h +++ b/ucspi-tcp-0.88/pathexec.h @@ -2,7 +2,7 @@ #define PATHEXEC_H extern void pathexec_run(char *,char **,char **); -extern int pathexec_env(char *,char *); +extern int pathexec_env(const char *,const char *); extern void pathexec(char **); #endif diff --git a/ucspi-tcp-0.88/pathexec_env.c b/ucspi-tcp-0.88/pathexec_env.c index 48bba7e..157e71b 100644 --- a/ucspi-tcp-0.88/pathexec_env.c +++ b/ucspi-tcp-0.88/pathexec_env.c @@ -8,7 +8,7 @@ static stralloc plus; static stralloc tmp; -int pathexec_env(char *s,char *t) +int pathexec_env(const char *s,const char *t) { if (!s) return 1; if (!stralloc_copys(&tmp,s)) return 0; @@ -22,7 +22,6 @@ int pathexec_env(char *s,char *t) void pathexec(char **argv) { - char *path; char **e; unsigned int elen; unsigned int i; diff --git a/ucspi-tcp-0.88/rblsmtpd.c b/ucspi-tcp-0.88/rblsmtpd.c index cc8ba2e..200a345 100644 --- a/ucspi-tcp-0.88/rblsmtpd.c +++ b/ucspi-tcp-0.88/rblsmtpd.c @@ -25,26 +25,58 @@ void usage(void) strerr_die1x(100,"rblsmtpd: usage: rblsmtpd [ -b ] [ -R ] [ -t timeout ] [ -r base ] [ -a base ] smtpd [ arg ... ]"); } +char *tcp_proto; char *ip_env; static stralloc ip_reverse; +static inline char tohex(char c) { + return c>=10?c-10+'a':c+'0'; +} + void ip_init(void) { unsigned int i; unsigned int j; + unsigned char remoteip[16]; + char hexval; + tcp_proto = env_get("PROTO"); + if (!tcp_proto) tcp_proto = ""; ip_env = env_get("TCPREMOTEIP"); if (!ip_env) ip_env = ""; if (!stralloc_copys(&ip_reverse,"")) nomem(); i = str_len(ip_env); - while (i) { - for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break; - if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) nomem(); - if (!stralloc_cats(&ip_reverse,".")) nomem(); - if (!j) break; - i = j - 1; + if (str_diff(tcp_proto, "TCP6") != 0) + { + // IPv4 + while (i) { + for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break; + if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) nomem(); + if (!stralloc_cats(&ip_reverse,".")) nomem(); + if (!j) break; + i = j - 1; + } + } + else + { + // IPv6 + if ((i=scan_ip6(ip_env, remoteip))==0) + return; + + for (j=16; j>0; j--) + { + hexval=tohex(remoteip[j-1] & 15); + if(!stralloc_catb(&ip_reverse, &hexval, 1)) nomem(); + if(!stralloc_cats(&ip_reverse, ".")) nomem(); + + hexval=tohex(remoteip[j-1] >> 4); + if(!stralloc_catb(&ip_reverse, &hexval, 1)) nomem(); + if(!stralloc_cats(&ip_reverse, ".")) nomem(); + } + + if(!stralloc_cats(&ip_reverse, "ipv6.")) nomem(); } } @@ -190,7 +222,7 @@ main(int argc,char **argv,char **envp) argv += optind; if (!*argv) usage(); - if (flagwantdefaultrbl) rbl("rbl.maps.vix.com"); + if (flagwantdefaultrbl) rbl("zen.spamhaus.org"); if (decision >= 2) rblsmtpd(); pathexec_run(*argv,argv,envp); diff --git a/ucspi-tcp-0.88/remoteinfo.h b/ucspi-tcp-0.88/remoteinfo.h index 2ca779d..0884cc1 100644 --- a/ucspi-tcp-0.88/remoteinfo.h +++ b/ucspi-tcp-0.88/remoteinfo.h @@ -5,5 +5,6 @@ #include "uint16.h" extern int remoteinfo(stralloc *,char *,uint16,char *,uint16,unsigned int); +extern int remoteinfo6(stralloc *,char *,uint16,char *,uint16,unsigned int,uint32); #endif diff --git a/ucspi-tcp-0.88/rules.c b/ucspi-tcp-0.88/rules.c index 1840360..4fc2354 100644 --- a/ucspi-tcp-0.88/rules.c +++ b/ucspi-tcp-0.88/rules.c @@ -64,7 +64,7 @@ static int doit(void (*callback)(char *,unsigned int),char *ip,char *host,char * if (!stralloc_copys(&rules_name,ip)) return -1; while (rules_name.len > 0) { - if (ip[rules_name.len - 1] == '.') { + if (ip[rules_name.len - 1] == '.' || ip[rules_name.len - 1] == ':') { r = dorule(callback); if (r) return r; } diff --git a/ucspi-tcp-0.88/socket.h b/ucspi-tcp-0.88/socket.h index 80fb260..4fba762 100644 --- a/ucspi-tcp-0.88/socket.h +++ b/ucspi-tcp-0.88/socket.h @@ -2,21 +2,52 @@ #define SOCKET_H #include "uint16.h" +#include "uint32.h" extern int socket_tcp(void); extern int socket_udp(void); +extern int socket_tcp6(void); +extern int socket_udp6(void); -extern int socket_connect4(int,char *,uint16); +extern int socket_connect4(int,const char *,uint16); +extern int socket_connect6(int s,const char *ip,uint16 port,uint32 scope_id); extern int socket_connected(int); -extern int socket_bind4(int,char *,uint16); -extern int socket_bind4_reuse(int,char *,uint16); +extern int socket_bind4(int,const char *,uint16); +extern int socket_bind4_reuse(int,const char *,uint16); +extern int socket_bind6(int s,const char *ip,uint16 port,uint32 scope_id); +extern int socket_bind6_reuse(int s,const char *ip,uint16 port,uint32 scope_id); extern int socket_listen(int,int); extern int socket_accept4(int,char *,uint16 *); +extern int socket_accept6(int s,char *ip,uint16 *port,uint32 *scope_id); extern int socket_recv4(int,char *,int,char *,uint16 *); -extern int socket_send4(int,char *,int,char *,uint16); +extern int socket_send4(int,const char *,int,const char *,uint16); +extern int socket_recv6(int s,char *buf,unsigned int len,char *ip,uint16 *port,uint32 *scope_id); +extern int socket_send6(int s,const char *buf,unsigned int len,const char *ip,uint16 port,uint32 scope_id); extern int socket_local4(int,char *,uint16 *); extern int socket_remote4(int,char *,uint16 *); +extern int socket_local6(int s,char *ip,uint16 *port,uint32 *scope_id); +extern int socket_remote6(int s,char *ip,uint16 *port,uint32 *scope_id); + +/* enable sending udp packets to the broadcast address */ +extern int socket_broadcast(int); +/* join a multicast group on the given interface */ +extern int socket_mcjoin4(int,char *,char *); +extern int socket_mcjoin6(int,char *,int); +/* leave a multicast group on the given interface */ +extern int socket_mcleave4(int,char *); +extern int socket_mcleave6(int,char *); +/* set multicast TTL/hop count for outgoing packets */ +extern int socket_mcttl4(int,char); +extern int socket_mcttl6(int,char); +/* enable multicast loopback */ +extern int socket_mcloop4(int,char); +extern int socket_mcloop6(int,char); + +extern const char* socket_getifname(uint32 interface); +extern uint32 socket_getifidx(const char *ifname); extern void socket_tryreservein(int,int); +extern int noipv6; + #endif diff --git a/ucspi-tcp-0.88/socket_bind.c b/ucspi-tcp-0.88/socket_bind.c index 20830a4..067b4a8 100644 --- a/ucspi-tcp-0.88/socket_bind.c +++ b/ucspi-tcp-0.88/socket_bind.c @@ -5,7 +5,7 @@ #include "byte.h" #include "socket.h" -int socket_bind4(int s,char ip[4],uint16 port) +int socket_bind4(int s,const char ip[4],uint16 port) { struct sockaddr_in sa; @@ -17,7 +17,7 @@ int socket_bind4(int s,char ip[4],uint16 port) return bind(s,(struct sockaddr *) &sa,sizeof sa); } -int socket_bind4_reuse(int s,char ip[4],uint16 port) +int socket_bind4_reuse(int s,const char ip[4],uint16 port) { int opt = 1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt); diff --git a/ucspi-tcp-0.88/socket_conn.c b/ucspi-tcp-0.88/socket_conn.c index 35adac4..dcc93ac 100644 --- a/ucspi-tcp-0.88/socket_conn.c +++ b/ucspi-tcp-0.88/socket_conn.c @@ -6,7 +6,7 @@ #include "byte.h" #include "socket.h" -int socket_connect4(int s,char ip[4],uint16 port) +int socket_connect4(int s,const char ip[4],uint16 port) { struct sockaddr_in sa; diff --git a/ucspi-tcp-0.88/str.h b/ucspi-tcp-0.88/str.h index ab4aedd..a2a4b75 100644 --- a/ucspi-tcp-0.88/str.h +++ b/ucspi-tcp-0.88/str.h @@ -1,13 +1,13 @@ #ifndef STR_H #define STR_H -extern unsigned int str_copy(char *,char *); -extern int str_diff(char *,char *); -extern int str_diffn(char *,char *,unsigned int); -extern unsigned int str_len(char *); -extern unsigned int str_chr(char *,int); -extern unsigned int str_rchr(char *,int); -extern int str_start(char *,char *); +extern unsigned int str_copy(char *,const char *); +extern int str_diff(const char *,const char *); +extern int str_diffn(const char *,const char *,unsigned int); +extern unsigned int str_len(const char *); +extern unsigned int str_chr(const char *,int); +extern unsigned int str_rchr(const char *,int); +extern int str_start(const char *,const char *); #define str_equal(s,t) (!str_diff((s),(t))) diff --git a/ucspi-tcp-0.88/str_chr.c b/ucspi-tcp-0.88/str_chr.c index 886d6b6..042dfa2 100644 --- a/ucspi-tcp-0.88/str_chr.c +++ b/ucspi-tcp-0.88/str_chr.c @@ -1,9 +1,9 @@ #include "str.h" -unsigned int str_chr(register char *s,int c) +unsigned int str_chr(register const char *s,int c) { register char ch; - register char *t; + register const char *t; ch = c; t = s; diff --git a/ucspi-tcp-0.88/str_diff.c b/ucspi-tcp-0.88/str_diff.c index 037dcdf..071e7f5 100644 --- a/ucspi-tcp-0.88/str_diff.c +++ b/ucspi-tcp-0.88/str_diff.c @@ -1,6 +1,6 @@ #include "str.h" -int str_diff(register char *s,register char *t) +int str_diff(register const char *s,register const char *t) { register char x; diff --git a/ucspi-tcp-0.88/str_len.c b/ucspi-tcp-0.88/str_len.c index 5bd3f62..8411ebf 100644 --- a/ucspi-tcp-0.88/str_len.c +++ b/ucspi-tcp-0.88/str_len.c @@ -1,8 +1,8 @@ #include "str.h" -unsigned int str_len(char *s) +unsigned int str_len(const char *s) { - register char *t; + register const char *t; t = s; for (;;) { diff --git a/ucspi-tcp-0.88/str_start.c b/ucspi-tcp-0.88/str_start.c index 43430bb..757189d 100644 --- a/ucspi-tcp-0.88/str_start.c +++ b/ucspi-tcp-0.88/str_start.c @@ -1,6 +1,6 @@ #include "str.h" -int str_start(register char *s,register char *t) +int str_start(register const char *s,register const char *t) { register char x; diff --git a/ucspi-tcp-0.88/stralloc.h b/ucspi-tcp-0.88/stralloc.h index 7866812..cc17048 100644 --- a/ucspi-tcp-0.88/stralloc.h +++ b/ucspi-tcp-0.88/stralloc.h @@ -9,18 +9,20 @@ extern int stralloc_ready(stralloc *,unsigned int); extern int stralloc_readyplus(stralloc *,unsigned int); extern int stralloc_copy(stralloc *,stralloc *); extern int stralloc_cat(stralloc *,stralloc *); -extern int stralloc_copys(stralloc *,char *); -extern int stralloc_cats(stralloc *,char *); -extern int stralloc_copyb(stralloc *,char *,unsigned int); -extern int stralloc_catb(stralloc *,char *,unsigned int); +extern int stralloc_copys(stralloc *,const char *); +extern int stralloc_cats(stralloc *,const char *); +extern int stralloc_copyb(stralloc *,const char *,unsigned int); +extern int stralloc_catb(stralloc *,const char *,unsigned int); extern int stralloc_append(stralloc *,char *); /* beware: this takes a pointer to 1 char */ -extern int stralloc_starts(stralloc *,char *); +extern int stralloc_starts(stralloc *,const char *); #define stralloc_0(sa) stralloc_append(sa,"") extern int stralloc_catulong0(stralloc *,unsigned long,unsigned int); extern int stralloc_catlong0(stralloc *,long,unsigned int); +extern void stralloc_free(stralloc *); + #define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0)) #define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n))) #define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n))) diff --git a/ucspi-tcp-0.88/stralloc_catb.c b/ucspi-tcp-0.88/stralloc_catb.c index b739bed..b606e32 100644 --- a/ucspi-tcp-0.88/stralloc_catb.c +++ b/ucspi-tcp-0.88/stralloc_catb.c @@ -1,7 +1,7 @@ #include "stralloc.h" #include "byte.h" -int stralloc_catb(stralloc *sa,char *s,unsigned int n) +int stralloc_catb(stralloc *sa,const char *s,unsigned int n) { if (!sa->s) return stralloc_copyb(sa,s,n); if (!stralloc_readyplus(sa,n + 1)) return 0; diff --git a/ucspi-tcp-0.88/stralloc_cats.c b/ucspi-tcp-0.88/stralloc_cats.c index 8b11e94..92cb66e 100644 --- a/ucspi-tcp-0.88/stralloc_cats.c +++ b/ucspi-tcp-0.88/stralloc_cats.c @@ -2,7 +2,7 @@ #include "str.h" #include "stralloc.h" -int stralloc_cats(stralloc *sa,char *s) +int stralloc_cats(stralloc *sa,const char *s) { return stralloc_catb(sa,s,str_len(s)); } diff --git a/ucspi-tcp-0.88/stralloc_opyb.c b/ucspi-tcp-0.88/stralloc_opyb.c index 46b99fc..593029d 100644 --- a/ucspi-tcp-0.88/stralloc_opyb.c +++ b/ucspi-tcp-0.88/stralloc_opyb.c @@ -1,7 +1,7 @@ #include "stralloc.h" #include "byte.h" -int stralloc_copyb(stralloc *sa,char *s,unsigned int n) +int stralloc_copyb(stralloc *sa,const char *s,unsigned int n) { if (!stralloc_ready(sa,n + 1)) return 0; byte_copy(sa->s,n,s); diff --git a/ucspi-tcp-0.88/stralloc_opys.c b/ucspi-tcp-0.88/stralloc_opys.c index 78594b0..860c7e0 100644 --- a/ucspi-tcp-0.88/stralloc_opys.c +++ b/ucspi-tcp-0.88/stralloc_opys.c @@ -2,7 +2,7 @@ #include "str.h" #include "stralloc.h" -int stralloc_copys(stralloc *sa,char *s) +int stralloc_copys(stralloc *sa,const char *s) { return stralloc_copyb(sa,s,str_len(s)); } diff --git a/ucspi-tcp-0.88/tcpclient.c b/ucspi-tcp-0.88/tcpclient.c index 9f6d7f2..77b1ad5 100644 --- a/ucspi-tcp-0.88/tcpclient.c +++ b/ucspi-tcp-0.88/tcpclient.c @@ -9,6 +9,7 @@ #include "scan.h" #include "str.h" #include "ip4.h" +#include "ip6.h" #include "uint16.h" #include "socket.h" #include "fd.h" @@ -20,6 +21,7 @@ #include "timeoutconn.h" #include "remoteinfo.h" #include "dns.h" +#include "byte.h" #define FATAL "tcpclient: fatal: " #define CONNECT "tcpclient: unable to connect to " @@ -31,27 +33,30 @@ void nomem(void) void usage(void) { strerr_die1x(100,"tcpclient: usage: tcpclient \ -[ -hHrRdDqQv ] \ +[ -46hHrRdDqQv ] \ [ -i localip ] \ [ -p localport ] \ [ -T timeoutconn ] \ [ -l localname ] \ [ -t timeoutinfo ] \ +[ -I interface ] \ host port program"); } +int forcev6 = 0; int verbosity = 1; int flagdelay = 1; int flagremoteinfo = 1; int flagremotehost = 1; unsigned long itimeout = 26; unsigned long ctimeout[2] = { 2, 58 }; +uint32 netif = 0; -char iplocal[4] = { 0,0,0,0 }; +char iplocal[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; uint16 portlocal = 0; char *forcelocal = 0; -char ipremote[4]; +char ipremote[16]; uint16 portremote; char *hostname; @@ -61,12 +66,13 @@ static stralloc moreaddresses; static stralloc tmp; static stralloc fqdn; char strnum[FMT_ULONG]; -char ipstr[IP4_FMT]; +char ipstr[IP6_FMT]; char seed[128]; main(int argc,char **argv) { + int fakev4=0; unsigned long u; int opt; char *x; @@ -80,8 +86,10 @@ main(int argc,char **argv) close(7); sig_ignore(sig_pipe); - while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:")) != opteof) + while ((opt = getopt(argc,argv,"46dDvqQhHrRi:p:t:T:l:I:")) != opteof) switch(opt) { + case '4': noipv6 = 1; break; + case '6': forcev6 = 1; break; case 'd': flagdelay = 1; break; case 'D': flagdelay = 0; break; case 'v': verbosity = 2; break; @@ -97,7 +105,8 @@ main(int argc,char **argv) if (optarg[j] == '+') ++j; scan_ulong(optarg + j,&ctimeout[1]); break; - case 'i': if (!ip4_scan(optarg,iplocal)) usage(); break; + case 'i': if (!scan_ip6(optarg,iplocal)) usage(); break; + case 'I': netif=socket_getifidx(optarg); break; case 'p': scan_ulong(optarg,&u); portlocal = u; break; default: usage(); } @@ -108,8 +117,8 @@ main(int argc,char **argv) hostname = *argv; if (!hostname) usage(); - if (str_equal(hostname,"")) hostname = "127.0.0.1"; - if (str_equal(hostname,"0")) hostname = "127.0.0.1"; + if (!hostname[0] || str_equal(hostname,"0")) + hostname = (noipv6?"127.0.0.1":"::1"); x = *++argv; if (!x) usage(); @@ -127,33 +136,36 @@ main(int argc,char **argv) if (!*++argv) usage(); if (!stralloc_copys(&tmp,hostname)) nomem(); - if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1) + if (dns_ip6_qualify(&addresses,&fqdn,&tmp) == -1) strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": "); - if (addresses.len < 4) + if (addresses.len < 16) strerr_die3x(111,FATAL,"no IP address for ",hostname); - if (addresses.len == 4) { + if (addresses.len == 16) { ctimeout[0] += ctimeout[1]; ctimeout[1] = 0; } for (cloop = 0;cloop < 2;++cloop) { if (!stralloc_copys(&moreaddresses,"")) nomem(); - for (j = 0;j + 4 <= addresses.len;j += 4) { - s = socket_tcp(); + for (j = 0;j + 16 <= addresses.len;j += 4) { + s = socket_tcp6(); if (s == -1) strerr_die2sys(111,FATAL,"unable to create socket: "); - if (socket_bind4(s,iplocal,portlocal) == -1) + if (socket_bind6(s,iplocal,portlocal,netif) == -1) strerr_die2sys(111,FATAL,"unable to bind socket: "); - if (timeoutconn(s,addresses.s + j,portremote,ctimeout[cloop]) == 0) + if (timeoutconn6(s,addresses.s + j,portremote,ctimeout[cloop],netif) == 0) goto CONNECTED; close(s); if (!cloop && ctimeout[1] && (errno == error_timeout)) { - if (!stralloc_catb(&moreaddresses,addresses.s + j,4)) nomem(); + if (!stralloc_catb(&moreaddresses,addresses.s + j,16)) nomem(); } else { strnum[fmt_ulong(strnum,portremote)] = 0; - ipstr[ip4_fmt(ipstr,addresses.s + j)] = 0; + if (ip6_isv4mapped(addresses.s+j)) + ipstr[ip4_fmt(ipstr,addresses.s + j + 12)] = 0; + else + ipstr[ip6_fmt(ipstr,addresses.s + j)] = 0; strerr_warn5(CONNECT,ipstr," port ",strnum,": ",&strerr_sys); } } @@ -169,37 +181,46 @@ main(int argc,char **argv) if (!flagdelay) socket_tcpnodelay(s); /* if it fails, bummer */ - if (!pathexec_env("PROTO","TCP")) nomem(); - - if (socket_local4(s,iplocal,&portlocal) == -1) + if (socket_local6(s,iplocal,&portlocal,&netif) == -1) strerr_die2sys(111,FATAL,"unable to get local address: "); + if (!forcev6 && (ip6_isv4mapped(iplocal) || byte_equal(iplocal,16,V6any))) + fakev4=1; + + if (!pathexec_env("PROTO",fakev4?"TCP":"TCP6")) nomem(); + strnum[fmt_ulong(strnum,portlocal)] = 0; if (!pathexec_env("TCPLOCALPORT",strnum)) nomem(); - ipstr[ip4_fmt(ipstr,iplocal)] = 0; + if (fakev4) + ipstr[ip4_fmt(ipstr,iplocal+12)] = 0; + else + ipstr[ip6_fmt(ipstr,iplocal)] = 0; if (!pathexec_env("TCPLOCALIP",ipstr)) nomem(); x = forcelocal; if (!x) - if (dns_name4(&tmp,iplocal) == 0) { + if (dns_name6(&tmp,iplocal) == 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } if (!pathexec_env("TCPLOCALHOST",x)) nomem(); - if (socket_remote4(s,ipremote,&portremote) == -1) + if (socket_remote6(s,ipremote,&portremote,&netif) == -1) strerr_die2sys(111,FATAL,"unable to get remote address: "); strnum[fmt_ulong(strnum,portremote)] = 0; if (!pathexec_env("TCPREMOTEPORT",strnum)) nomem(); - ipstr[ip4_fmt(ipstr,ipremote)] = 0; + if (fakev4) + ipstr[ip4_fmt(ipstr,ipremote+12)] = 0; + else + ipstr[ip6_fmt(ipstr,ipremote)] = 0; if (!pathexec_env("TCPREMOTEIP",ipstr)) nomem(); if (verbosity >= 2) strerr_warn4("tcpclient: connected to ",ipstr," port ",strnum,0); x = 0; if (flagremotehost) - if (dns_name4(&tmp,ipremote) == 0) { + if (dns_name6(&tmp,ipremote) == 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } @@ -207,7 +228,7 @@ main(int argc,char **argv) x = 0; if (flagremoteinfo) - if (remoteinfo(&tmp,ipremote,portremote,iplocal,portlocal,itimeout) == 0) { + if (remoteinfo6(&tmp,ipremote,portremote,iplocal,portlocal,itimeout,netif) == 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } diff --git a/ucspi-tcp-0.88/tcprules.c b/ucspi-tcp-0.88/tcprules.c index a684ac5..83519c8 100644 --- a/ucspi-tcp-0.88/tcprules.c +++ b/ucspi-tcp-0.88/tcprules.c @@ -123,8 +123,15 @@ main(int argc,char **argv) } line.len = len; /* for die_bad() */ - colon = byte_chr(x,len,':'); - if (colon == len) continue; + colon = 0; + for (;;) { + int tmp; + tmp = byte_chr(x + colon,len - colon,':'); + colon += tmp; + if (colon == len) continue; + if (byte_equal(x+colon+1,4,"deny") || byte_equal(x+colon+1,5,"allow")) break; + ++colon; + } if (!stralloc_copyb(&address,x,colon)) nomem(); if (!stralloc_copys(&data,"")) nomem(); diff --git a/ucspi-tcp-0.88/tcpserver.c b/ucspi-tcp-0.88/tcpserver.c index 979a0be..aab637f 100644 --- a/ucspi-tcp-0.88/tcpserver.c +++ b/ucspi-tcp-0.88/tcpserver.c @@ -7,6 +7,7 @@ #include "fmt.h" #include "scan.h" #include "ip4.h" +#include "ip6.h" #include "fd.h" #include "exit.h" #include "env.h" @@ -28,6 +29,7 @@ #include "sig.h" #include "dns.h" +int forcev6 = 0; int verbosity = 1; int flagkillopts = 1; int flagdelay = 1; @@ -36,20 +38,21 @@ int flagremoteinfo = 1; int flagremotehost = 1; int flagparanoid = 0; unsigned long timeout = 26; +uint32 netif = 0; static stralloc tcpremoteinfo; uint16 localport; char localportstr[FMT_ULONG]; -char localip[4]; -char localipstr[IP4_FMT]; +char localip[16]; +char localipstr[IP6_FMT]; static stralloc localhostsa; char *localhost = 0; uint16 remoteport; char remoteportstr[FMT_ULONG]; -char remoteip[4]; -char remoteipstr[IP4_FMT]; +char remoteip[16]; +char remoteipstr[IP6_FMT]; static stralloc remotehostsa; char *remotehost = 0; @@ -96,12 +99,12 @@ void safecats(char *s) if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; if (ch == '%') ch = '?'; /* logger stupidity */ - if (ch == ':') ch = '?'; +/* if (ch == ':') ch = '?'; */ append(&ch); } cats("..."); } -void env(char *s,char *t) +void env(const char *s,const char *t) { if (!pathexec_env(s,t)) drop_nomem(); } @@ -135,9 +138,16 @@ void found(char *data,unsigned int datalen) void doit(int t) { + int fakev4=0; int j; + uint32 scope_id; - remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; + if (!forcev6 && ip6_isv4mapped(remoteip)) + fakev4=1; + if (fakev4) + remoteipstr[ip4_fmt(remoteipstr,remoteip+12)] = 0; + else + remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0; if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; @@ -155,30 +165,40 @@ void doit(int t) strerr_die2sys(111,DROP,"unable to print banner: "); } - if (socket_local4(t,localip,&localport) == -1) + if (socket_local6(t,localip,&localport,&scope_id) == -1) strerr_die2sys(111,DROP,"unable to get local address: "); - localipstr[ip4_fmt(localipstr,localip)] = 0; + if (fakev4) + localipstr[ip4_fmt(localipstr,localip+12)] = 0; + else + localipstr[ip6_fmt(localipstr,localip)] = 0; remoteportstr[fmt_ulong(remoteportstr,remoteport)] = 0; if (!localhost) - if (dns_name4(&localhostsa,localip) == 0) + if (dns_name6(&localhostsa,localip) == 0) if (localhostsa.len) { if (!stralloc_0(&localhostsa)) drop_nomem(); localhost = localhostsa.s; } - env("PROTO","TCP"); + env("PROTO",fakev4?"TCP":"TCP6"); env("TCPLOCALIP",localipstr); + localipstr[ip6_fmt(localipstr,localip)]=0; + env("TCP6LOCALIP",localipstr); + env("TCPLOCALPORT",localportstr); + env("TCP6LOCALPORT",localportstr); env("TCPLOCALHOST",localhost); + env("TCP6LOCALHOST",localhost); + if (!fakev4 && scope_id) + env("TCP6INTERFACE",socket_getifname(scope_id)); if (flagremotehost) - if (dns_name4(&remotehostsa,remoteip) == 0) + if (dns_name6(&remotehostsa,remoteip) == 0) if (remotehostsa.len) { if (flagparanoid) - if (dns_ip4(&tmp,&remotehostsa) == 0) - for (j = 0;j + 4 <= tmp.len;j += 4) - if (byte_equal(remoteip,4,tmp.s + j)) { + if (dns_ip6(&tmp,&remotehostsa) == 0) + for (j = 0;j + 16 <= tmp.len;j += 16) + if (byte_equal(remoteip,16,tmp.s + j)) { flagparanoid = 0; break; } @@ -188,15 +208,20 @@ void doit(int t) } } env("TCPREMOTEIP",remoteipstr); + remoteipstr[ip6_fmt(remoteipstr,remoteip)]=0; + env("TCP6REMOTEIP",remoteipstr); env("TCPREMOTEPORT",remoteportstr); + env("TCP6REMOTEPORT",remoteportstr); env("TCPREMOTEHOST",remotehost); + env("TCP6REMOTEHOST",remotehost); if (flagremoteinfo) { - if (remoteinfo(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout) == -1) + if (remoteinfo6(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout,netif) == -1) flagremoteinfo = 0; if (!stralloc_0(&tcpremoteinfo)) drop_nomem(); } env("TCPREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0); + env("TCP6REMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0); if (fnrules) { int fdrules; @@ -206,7 +231,15 @@ void doit(int t) if (!flagallownorules) drop_rules(); } else { - if (rules(found,fdrules,remoteipstr,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1) drop_rules(); + int fakev4=0; + char* temp; + if (!forcev6 && ip6_isv4mapped(remoteip)) + fakev4=1; + if (fakev4) + temp=remoteipstr+7; + else + temp=remoteipstr; + if (rules(found,fdrules,temp,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1) drop_rules(); close(fdrules); } } @@ -240,7 +273,7 @@ void usage(void) { strerr_warn1("\ tcpserver: usage: tcpserver \ -[ -1UXpPhHrRoOdDqQv ] \ +[ -461UXpPhHrRoOdDqQv ] \ [ -c limit ] \ [ -x rules.cdb ] \ [ -B banner ] \ @@ -249,6 +282,7 @@ tcpserver: usage: tcpserver \ [ -b backlog ] \ [ -l localname ] \ [ -t timeout ] \ +[ -I interface ] \ host port program",0); _exit(100); } @@ -299,8 +333,8 @@ main(int argc,char **argv) unsigned long u; int s; int t; - - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof) + + while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; @@ -325,7 +359,10 @@ main(int argc,char **argv) x = env_get("GID"); if (x) scan_ulong(x,&gid); break; case 'u': scan_ulong(optarg,&uid); break; case 'g': scan_ulong(optarg,&gid); break; + case 'I': netif=socket_getifidx(optarg); break; case '1': flag1 = 1; break; + case '4': noipv6 = 1; break; + case '6': forcev6 = 1; break; case 'l': localhost = optarg; break; default: usage(); } @@ -337,8 +374,7 @@ main(int argc,char **argv) hostname = *argv++; if (!hostname) usage(); - if (str_equal(hostname,"")) hostname = "0.0.0.0"; - if (str_equal(hostname,"0")) hostname = "0.0.0.0"; + if (str_equal(hostname,"")) hostname = "0"; x = *argv++; if (!x) usage(); @@ -348,7 +384,7 @@ main(int argc,char **argv) se = getservbyname(x,"tcp"); if (!se) strerr_die3x(111,FATAL,"unable to figure out port number for ",x); - localport = ntohs(se->s_port); + uint16_unpack_big((char*)&se->s_port,&localport); } if (!*argv) usage(); @@ -358,20 +394,26 @@ main(int argc,char **argv) sig_catch(sig_term,sigterm); sig_ignore(sig_pipe); - if (!stralloc_copys(&tmp,hostname)) - strerr_die2x(111,FATAL,"out of memory"); - if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1) - strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": "); - if (addresses.len < 4) - strerr_die3x(111,FATAL,"no IP address for ",hostname); - byte_copy(localip,4,addresses.s); - - s = socket_tcp(); + if (str_equal(hostname,"0")) { + byte_zero(localip,sizeof localip); + } else { + if (!stralloc_copys(&tmp,hostname)) + strerr_die2x(111,FATAL,"out of memory"); + if (dns_ip6_qualify(&addresses,&fqdn,&tmp) == -1) + strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": "); + if (addresses.len < 16) + strerr_die3x(111,FATAL,"no IP address for ",hostname); + byte_copy(localip,16,addresses.s); + if (ip6_isv4mapped(localip)) + noipv6=1; + } + + s = socket_tcp6(); if (s == -1) strerr_die2sys(111,FATAL,"unable to create socket: "); - if (socket_bind4_reuse(s,localip,localport) == -1) + if (socket_bind6_reuse(s,localip,localport,netif) == -1) strerr_die2sys(111,FATAL,"unable to bind: "); - if (socket_local4(s,localip,&localport) == -1) + if (socket_local6(s,localip,&localport,&netif) == -1) strerr_die2sys(111,FATAL,"unable to get local address: "); if (socket_listen(s,backlog) == -1) strerr_die2sys(111,FATAL,"unable to listen: "); @@ -399,7 +441,7 @@ main(int argc,char **argv) while (numchildren >= limit) sig_pause(); sig_unblock(sig_child); - t = socket_accept4(s,remoteip,&remoteport); + t = socket_accept6(s,remoteip,&remoteport,&netif); sig_block(sig_child); if (t == -1) continue; diff --git a/ucspi-tcp-0.88/timeoutconn.h b/ucspi-tcp-0.88/timeoutconn.h index 7f9dcc9..01e6a75 100644 --- a/ucspi-tcp-0.88/timeoutconn.h +++ b/ucspi-tcp-0.88/timeoutconn.h @@ -2,7 +2,9 @@ #define TIMEOUTCONN_H #include "uint16.h" +#include "uint32.h" extern int timeoutconn(int,char *,uint16,unsigned int); +extern int timeoutconn6(int,char *,uint16,unsigned int,uint32); #endif -- cgit v1.2.3 From 01ef1d365e5776b130e8b65772a80b6adcb0ed46 Mon Sep 17 00:00:00 2001 From: John Denker Date: Thu, 26 Jul 2012 11:00:48 -0700 Subject: files added by IPv6 patch --- ucspi-tcp-0.88/addcr.1 | 22 +++++++++ ucspi-tcp-0.88/argv0.1 | 47 ++++++++++++++++++++ ucspi-tcp-0.88/date@.1 | 32 ++++++++++++++ ucspi-tcp-0.88/delcr.1 | 30 +++++++++++++ ucspi-tcp-0.88/dns_ip6.c | 103 +++++++++++++++++++++++++++++++++++++++++++ ucspi-tcp-0.88/dns_ipq6.c | 72 ++++++++++++++++++++++++++++++ ucspi-tcp-0.88/dns_nd6.c | 28 ++++++++++++ ucspi-tcp-0.88/dns_sortip6.c | 20 +++++++++ ucspi-tcp-0.88/finger@.1 | 45 +++++++++++++++++++ ucspi-tcp-0.88/fixcr.1 | 11 +++++ ucspi-tcp-0.88/fmt_xlong.c | 22 +++++++++ ucspi-tcp-0.88/haveip6.h | 1 + ucspi-tcp-0.88/haveip6.h1 | 1 + ucspi-tcp-0.88/haveip6.h2 | 1 + ucspi-tcp-0.88/http@.1 | 52 ++++++++++++++++++++++ ucspi-tcp-0.88/ip6.h | 28 ++++++++++++ 16 files changed, 515 insertions(+) create mode 100644 ucspi-tcp-0.88/addcr.1 create mode 100644 ucspi-tcp-0.88/argv0.1 create mode 100644 ucspi-tcp-0.88/date@.1 create mode 100644 ucspi-tcp-0.88/delcr.1 create mode 100644 ucspi-tcp-0.88/dns_ip6.c create mode 100644 ucspi-tcp-0.88/dns_ipq6.c create mode 100644 ucspi-tcp-0.88/dns_nd6.c create mode 100644 ucspi-tcp-0.88/dns_sortip6.c create mode 100644 ucspi-tcp-0.88/finger@.1 create mode 100644 ucspi-tcp-0.88/fixcr.1 create mode 100644 ucspi-tcp-0.88/fmt_xlong.c create mode 100644 ucspi-tcp-0.88/haveip6.h create mode 100644 ucspi-tcp-0.88/haveip6.h1 create mode 100644 ucspi-tcp-0.88/haveip6.h2 create mode 100644 ucspi-tcp-0.88/http@.1 create mode 100644 ucspi-tcp-0.88/ip6.h diff --git a/ucspi-tcp-0.88/addcr.1 b/ucspi-tcp-0.88/addcr.1 new file mode 100644 index 0000000..3bae1f7 --- /dev/null +++ b/ucspi-tcp-0.88/addcr.1 @@ -0,0 +1,22 @@ +.TH addcr 1 +.SH NAME +addcr \- add a CR before each LF +.SH SYNOPSIS +.B addcr +.SH DESCRIPTION +.B addcr +inserts CR at the end of each line of input. +It does not insert CR at the end of a partial final line. +.SH COMPATIBILITY +Some vendors ship +.B unix2dos +or +.B bsd2dos +tools similar to +.BR addcr . +Those tools often blow up on long lines and nulls. +.B addcr +has no trouble with long lines and nulls. +.SH "SEE ALSO" +delcr(1), +fixcr(1) diff --git a/ucspi-tcp-0.88/argv0.1 b/ucspi-tcp-0.88/argv0.1 new file mode 100644 index 0000000..ad9634d --- /dev/null +++ b/ucspi-tcp-0.88/argv0.1 @@ -0,0 +1,47 @@ +.TH argv0 1 +.SH NAME +argv0 \- run a program with a specified 0th argument +.SH SYNOPSIS +.B argv0 +.I realname +.I zero +[ +.I arg ... +] +.SH DESCRIPTION +.B argv0 +runs +the program stored as +.I realname +on disk, +with the given +arguments. +It sets the 0th argument of +the program to +.IR zero . + +For example, + +.EX + argv0 /bin/csh -bin/csh +.EE + +runs +.B /bin/csh +with a 0th argument of +.BR -bin/csh . +.B csh +will think it is a login shell +and behave accordingly. + +.B argv0 +can be used to run some +.B inetd +wrappers under +.BR tcpserver . +.SH "SEE ALSO" +csh(1), +tcpserver(1), +execve(2), +execvp(3), +inetd(8) diff --git a/ucspi-tcp-0.88/date@.1 b/ucspi-tcp-0.88/date@.1 new file mode 100644 index 0000000..fa0ba98 --- /dev/null +++ b/ucspi-tcp-0.88/date@.1 @@ -0,0 +1,32 @@ +.TH date@ 1 +.SH NAME +date@ \- print the date on a host +.SH SYNTAX +.B date@ +[ +.I host +] +.SH DESCRIPTION +.B date@ +connects to TCP port 13 (Daytime) on +.I host +and prints any data it receives. +It removes CR and converts unprintable characters to a visible format. + +If +.I host +is not supplied, +.B date@ +connects to the local host. + +Some computers respond to port 13 with a human-readable date. +For example, they may be running + +.EX + tcpserver 0 13 date & +.EE +.SH "SEE ALSO" +cat(1), +delcr(1), +tcpclient(1), +tcpserver(1) diff --git a/ucspi-tcp-0.88/delcr.1 b/ucspi-tcp-0.88/delcr.1 new file mode 100644 index 0000000..18ea736 --- /dev/null +++ b/ucspi-tcp-0.88/delcr.1 @@ -0,0 +1,30 @@ +.TH delcr 1 +.SH NAME +delcr \- remove a CR before each LF +.SH SYNOPSIS +.B delcr +.SH DESCRIPTION +.B delcr +removes a CR at the end of each line of input, +if a CR is present. +It also removes a CR at the end of a partial final line. + +The pipeline + +.EX + addcr | delcr +.EE + +prints an exact copy of its input. +.SH COMPATIBILITY +Some vendors ship +.B dos2unix +or +.B dos2bsd +tools similar to +.BR delcr . +Those tools often blow up on long lines and nulls. +.B delcr +has no trouble with long lines and nulls. +.SH "SEE ALSO" +addcr(1) diff --git a/ucspi-tcp-0.88/dns_ip6.c b/ucspi-tcp-0.88/dns_ip6.c new file mode 100644 index 0000000..1a2ce08 --- /dev/null +++ b/ucspi-tcp-0.88/dns_ip6.c @@ -0,0 +1,103 @@ +#include "stralloc.h" +#include "uint16.h" +#include "byte.h" +#include "dns.h" +#include "ip4.h" +#include "ip6.h" + +static int dns_ip6_packet_add(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[16]; + uint16 numanswers; + uint16 datalen; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_AAAA)) { + if (byte_equal(header + 2,2,DNS_C_IN)) + if (datalen == 16) { + if (!dns_packet_copy(buf,len,pos,header,16)) return -1; + if (!stralloc_catb(out,header,16)) return -1; + } + } else if (byte_equal(header,2,DNS_T_A)) + if (byte_equal(header + 2,2,DNS_C_IN)) + if (datalen == 4) { + byte_copy(header,12,V4mappedprefix); + if (!dns_packet_copy(buf,len,pos,header+12,4)) return -1; + if (!stralloc_catb(out,header,16)) return -1; + } + pos += datalen; + } + + dns_sortip6(out->s,out->len); + return 0; +} + +int dns_ip6_packet(stralloc *out,const char *buf,unsigned int len) { + if (!stralloc_copys(out,"")) return -1; + return dns_ip6_packet_add(out,buf,len); +} + +static char *q = 0; + +int dns_ip6(stralloc *out,stralloc *fqdn) +{ + unsigned int i; + char code; + char ch; + char ip[16]; + + if (!stralloc_copys(out,"")) return -1; + if (!stralloc_readyplus(fqdn,1)) return -1; + fqdn->s[fqdn->len]=0; + if ((i=scan_ip6(fqdn->s,ip))) { + if (fqdn->s[i]) return -1; + stralloc_copyb(out,ip,16); + return 0; + } + code = 0; + for (i = 0;i <= fqdn->len;++i) { + if (i < fqdn->len) + ch = fqdn->s[i]; + else + ch = '.'; + + if ((ch == '[') || (ch == ']')) continue; + if (ch == '.') { + if (!stralloc_append(out,&code)) return -1; + code = 0; + continue; + } + if ((ch >= '0') && (ch <= '9')) { + code *= 10; + code += ch - '0'; + continue; + } + + if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1; + if (!stralloc_copys(out,"")) return -1; + if (dns_resolve(q,DNS_T_AAAA) != -1) + if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) { + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + } + if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1; + if (dns_resolve(q,DNS_T_A) != -1) + if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) { + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + } + return out->a>0?0:-1; + } + + out->len &= ~3; + return 0; +} diff --git a/ucspi-tcp-0.88/dns_ipq6.c b/ucspi-tcp-0.88/dns_ipq6.c new file mode 100644 index 0000000..d5cea12 --- /dev/null +++ b/ucspi-tcp-0.88/dns_ipq6.c @@ -0,0 +1,72 @@ +#include "stralloc.h" +#include "case.h" +#include "byte.h" +#include "str.h" +#include "dns.h" + +static int doit(stralloc *work,const char *rule) +{ + char ch; + unsigned int colon; + unsigned int prefixlen; + + ch = *rule++; + if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1; + colon = str_chr(rule,':'); + if (!rule[colon]) return 1; + + if (work->len < colon) return 1; + prefixlen = work->len - colon; + if ((ch == '=') && prefixlen) return 1; + if (case_diffb(rule,colon,work->s + prefixlen)) return 1; + if (ch == '?') { + if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1; + if (byte_chr(work->s,prefixlen,':') < prefixlen) return 1; + if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1; + if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1; + } + + work->len = prefixlen; + if (ch == '-') work->len = 0; + return stralloc_cats(work,rule + colon + 1); +} + +int dns_ip6_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules) +{ + unsigned int i; + unsigned int j; + unsigned int plus; + unsigned int fqdnlen; + + if (!stralloc_copy(fqdn,in)) return -1; + + for (j = i = 0;j < rules->len;++j) + if (!rules->s[j]) { + if (!doit(fqdn,rules->s + i)) return -1; + i = j + 1; + } + + fqdnlen = fqdn->len; + plus = byte_chr(fqdn->s,fqdnlen,'+'); + if (plus >= fqdnlen) + return dns_ip6(out,fqdn); + + i = plus + 1; + for (;;) { + j = byte_chr(fqdn->s + i,fqdnlen - i,'+'); + byte_copy(fqdn->s + plus,j,fqdn->s + i); + fqdn->len = plus + j; + if (dns_ip6(out,fqdn) == -1) return -1; + if (out->len) return 0; + i += j; + if (i >= fqdnlen) return 0; + ++i; + } +} + +int dns_ip6_qualify(stralloc *out,stralloc *fqdn,const stralloc *in) +{ + static stralloc rules; + if (dns_resolvconfrewrite(&rules) == -1) return -1; + return dns_ip6_qualify_rules(out,fqdn,in,&rules); +} diff --git a/ucspi-tcp-0.88/dns_nd6.c b/ucspi-tcp-0.88/dns_nd6.c new file mode 100644 index 0000000..fb1da88 --- /dev/null +++ b/ucspi-tcp-0.88/dns_nd6.c @@ -0,0 +1,28 @@ +#include "byte.h" +#include "fmt.h" +#include "dns.h" + +/* RFC1886: + * 4321:0:1:2:3:4:567:89ab + * -> + * b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.INT. + */ + +static inline char tohex(char c) { + return c>=10?c-10+'a':c+'0'; +} + +int dns_name6_domain(char name[DNS_NAME6_DOMAIN],char ip[16]) +{ + unsigned int j; + + for (j=0; j<16; j++) { + name[j*4]=1; + name[j*4+1]=tohex(ip[15-j] & 15); + name[j*4+2]=1; + name[j*4+3]=tohex((unsigned char)ip[15-j] >> 4); + } + byte_copy(name + 4*16,10,"\3ip6\4arpa\0"); + return 4*16+10; +} + diff --git a/ucspi-tcp-0.88/dns_sortip6.c b/ucspi-tcp-0.88/dns_sortip6.c new file mode 100644 index 0000000..7e752e9 --- /dev/null +++ b/ucspi-tcp-0.88/dns_sortip6.c @@ -0,0 +1,20 @@ +#include "byte.h" +#include "dns.h" + +/* XXX: sort servers by configurable notion of closeness? */ +/* XXX: pay attention to competence of each server? */ + +void dns_sortip6(char *s,unsigned int n) +{ + unsigned int i; + char tmp[16]; + + n >>= 4; + while (n > 1) { + i = dns_random(n); + --n; + byte_copy(tmp,16,s + (i << 4)); + byte_copy(s + (i << 4),16,s + (n << 4)); + byte_copy(s + (n << 4),16,tmp); + } +} diff --git a/ucspi-tcp-0.88/finger@.1 b/ucspi-tcp-0.88/finger@.1 new file mode 100644 index 0000000..93b6288 --- /dev/null +++ b/ucspi-tcp-0.88/finger@.1 @@ -0,0 +1,45 @@ +.TH finger@ 1 +.SH NAME +finger@ \- get user information from a host +.SH SYNTAX +.B finger@ +[ +.I host +[ +.I user +] +] +.SH DESCRIPTION +.B finger@ +connects to TCP port 79 (Finger) on +.IR host , +sends +.I user +(with an extra CR) +to +.IR host , +and prints any data it receives. +It removes CR and converts unprintable characters to a visible format. +Some computers respond to port 79 with information about +.IR user . + +If +.I user +is not supplied, +.B finger@ +sends a blank line to +.IR host . +Some computers respond with information about +all the users who are logged in. + +If +.I host +is not supplied, +.B finger@ +connects to the local host. +.SH "SEE ALSO" +addcr(1), +cat(1), +delcr(1), +finger(1), +tcpclient(1) diff --git a/ucspi-tcp-0.88/fixcr.1 b/ucspi-tcp-0.88/fixcr.1 new file mode 100644 index 0000000..ebb8b53 --- /dev/null +++ b/ucspi-tcp-0.88/fixcr.1 @@ -0,0 +1,11 @@ +.TH fixcr 1 +.SH NAME +fixcr \- make sure that there is a CR before each LF +.SH SYNOPSIS +.B fixcr +.SH DESCRIPTION +.B fixcr +inserts CR at the end of each line of input where a CR is not already present. +It does not insert CR at the end of a partial final line. +.SH "SEE ALSO" +addcr(1) diff --git a/ucspi-tcp-0.88/fmt_xlong.c b/ucspi-tcp-0.88/fmt_xlong.c new file mode 100644 index 0000000..332fc9a --- /dev/null +++ b/ucspi-tcp-0.88/fmt_xlong.c @@ -0,0 +1,22 @@ +#include "fmt.h" + +char tohex(char num) { + if (num<10) + return num+'0'; + else if (num<16) + return num-10+'a'; + else + return -1; +} + +unsigned int fmt_xlong(register char *s,register unsigned long u) +{ + register unsigned int len; register unsigned long q; + len = 1; q = u; + while (q > 15) { ++len; q /= 16; } + if (s) { + s += len; + do { *--s = tohex(u % 16); u /= 16; } while(u); /* handles u == 0 */ + } + return len; +} diff --git a/ucspi-tcp-0.88/haveip6.h b/ucspi-tcp-0.88/haveip6.h new file mode 100644 index 0000000..5564de9 --- /dev/null +++ b/ucspi-tcp-0.88/haveip6.h @@ -0,0 +1 @@ +#define LIBC_HAS_IP6 1 diff --git a/ucspi-tcp-0.88/haveip6.h1 b/ucspi-tcp-0.88/haveip6.h1 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ucspi-tcp-0.88/haveip6.h1 @@ -0,0 +1 @@ + diff --git a/ucspi-tcp-0.88/haveip6.h2 b/ucspi-tcp-0.88/haveip6.h2 new file mode 100644 index 0000000..5564de9 --- /dev/null +++ b/ucspi-tcp-0.88/haveip6.h2 @@ -0,0 +1 @@ +#define LIBC_HAS_IP6 1 diff --git a/ucspi-tcp-0.88/http@.1 b/ucspi-tcp-0.88/http@.1 new file mode 100644 index 0000000..4861b34 --- /dev/null +++ b/ucspi-tcp-0.88/http@.1 @@ -0,0 +1,52 @@ +.TH http@ 1 +.SH NAME +http@ \- get a web page from a host through HTTP +.SH SYNTAX +.B http@ +[ +.I host +[ +.I page +[ +.I port +] +] +] +.SH DESCRIPTION +.B http@ +connects to +.I port +on +.IR host , +sends +.B GET /\fIpage +(with an extra CR) +to +.IR host , +and prints any data it receives, +removing CR from the end of each line. + +If +.I port +is not supplied, +.B http@ +uses port 80 (HTTP). + +If +.I page +is not supplied, +.B http@ +sends +.B GET / +to +.IR host . + +If +.I host +is not supplied, +.B http@ +connects to the local host. +.SH "SEE ALSO" +addcr(1), +delcr(1), +tcpclient(1) diff --git a/ucspi-tcp-0.88/ip6.h b/ucspi-tcp-0.88/ip6.h new file mode 100644 index 0000000..88ff120 --- /dev/null +++ b/ucspi-tcp-0.88/ip6.h @@ -0,0 +1,28 @@ +#ifndef IP6_H +#define IP6_H + +#include "byte.h" + +extern unsigned int scan_ip6(const char *src,char *ip); +extern unsigned int fmt_ip6(char *dest,const char *ip); + +extern unsigned int scan_ip6_flat(const char *src,char *); +extern unsigned int fmt_ip6_flat(char *dest,const char *); + +/* + ip6 address syntax: (h = hex digit), no leading '0' required + 1. hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh + 2. any number of 0000 may be abbreviated as "::", but only once + flat ip6 address syntax: + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + */ + +#define IP6_FMT 40 + +extern const unsigned char V4mappedprefix[12]; /*={0,0,0,0,0,0,0,0,0,0,0xff,0xff}; */ +extern const unsigned char V6loopback[16]; /*={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; */ +extern const unsigned char V6any[16]; /*={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; */ + +#define ip6_isv4mapped(ip) (byte_equal(ip,12,V4mappedprefix)) + +#endif -- cgit v1.2.3 From 92280557b6bdde0b3e1d8fa93ba2fae494d5c355 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 15:58:57 -0700 Subject: clean up error reporting --- tools/hi-q.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/tools/hi-q.c b/tools/hi-q.c index 502de69..59cad5d 100644 --- a/tools/hi-q.c +++ b/tools/hi-q.c @@ -144,7 +144,7 @@ int Execve(char const * fn, int fork_and_wait(const jobber job){ pid_t kidpid = fork(); if (kidpid == -1) { - cerr << "hi-q: fork failed : "; + cerr << progid << " fork failed : "; perror(0); exit(ex_syserr); } @@ -157,9 +157,9 @@ int fork_and_wait(const jobber job){ if (!kidpid){ /*** child code ***/ - int rslt; - rslt = Execve(prog[0], prog, environ); - fprintf(stderr, "hi-q: failed to exec '%s': ", prog[0]); + Execve(prog[0], prog, environ); + cerr << progid << " failed to exec '" + << prog[0] << "' : " << endl; perror(0); exit(ex_syserr); } else { @@ -167,6 +167,11 @@ int fork_and_wait(const jobber job){ int kidstatus; pid_t somekid; somekid = waitpid(kidpid, &kidstatus, WUNTRACED); + if (somekid < 0) { + cerr << progid << " ??? waitpid failed : "; + perror(0); + return(ex_syserr); + } if (WIFEXITED(kidstatus)) { int sts = WEXITSTATUS(kidstatus); if (sts != ex_good && sts != ex_spam) { @@ -179,7 +184,7 @@ int fork_and_wait(const jobber job){ } else if (WIFSIGNALED(kidstatus)) { int sig = WTERMSIG(kidstatus); if (sig == SIGUSR1) {/* normal, no logging required */} - else cerr << "hi-q: job " << prog[0] + else cerr << progid << " job " << prog[0] << " killed by signal " << sig << endl; return(ex_syserr); } else { @@ -199,8 +204,8 @@ int fork_and_wait(vector post){ } void exeunt(const int sts) { - // FIXME: stop other children - //xxxx cerr << "hi-q: exeunt called with " << sts << endl; + // FIXME: stop other children, maybe? + //xxxx cerr << progid << " exeunt called with " << sts << endl; if (sts == ex_spam) fork_and_wait(post); if (sts == ex_penaltybox) exit(ex_spam); exit(sts); @@ -336,7 +341,9 @@ void attach(const int pipe_end, const int fd, const int kidno){ if (pipe_end != fd) { int rslt = dup2(pipe_end, fd); if (rslt < 0) { - fprintf(stderr, "hi-q: dup2(%d,%d) failed for kid %d : ", pipe_end, fd, kidno); + cerr << progid << " dup2(" << pipe_end + << "," << fd << ")" + " failed for kid " << kidno << " : "; perror(0); exit(ex_syserr); } @@ -394,7 +401,7 @@ bar ifstream conf; conf.open(conf_name); if (! conf.good()) { - cerr << "hi-q: could not open filter.conf file '" + cerr << progid << " could not open filter.conf file '" << conf_name << "'" << endl; exit(1); } @@ -431,7 +438,7 @@ bar if (nkids == 0) exit(0); // nothing to do if (verbose) for (unsigned int ii = 0; ii < nkids; ii++) { - cerr << "hi-q filter[" << ii << "] :; "; + cerr << progid << " filter[" << ii << "] :; "; for (VS::const_iterator token = filter[ii].cmd.begin(); token != filter[ii].cmd.end(); token++){ cerr << *token << " "; @@ -508,7 +515,7 @@ bar block_fd(blockme); rslt = pipe(datapipe); if (rslt < 0) { - fprintf(stderr, "hi-q: could not create datapipe: "); + cerr << progid << " could not create datapipe : "; perror(0); exeunt(ex_syserr); } @@ -559,7 +566,7 @@ bar kidpid[ii] = fork(); if (kidpid[ii] == -1) { - cerr << "hi-q: fork failed : "; + cerr << progid << " fork failed : "; perror(0); exit(ex_syserr); } @@ -657,14 +664,15 @@ bar exit(1); } rslt = Execve(prog[0], prog, environ); - fprintf(stderr, "hi-q: failed to exec '%s': ", prog[0]); + cerr << progid << " failed to exec '" + << prog[0] << "' : "; perror(0); exit(ex_syserr); } /*** parent code ***/ if (kidpid[ii] < 0) { - fprintf(stderr, "hi-q: failure to fork kid#%d: ", ii); + cerr << " failure to fork kid#" << ii << " : "; perror(0); exeunt(ex_syserr); } @@ -714,7 +722,7 @@ bar // so we don't need it. if (verbose) for (unsigned int ii = 0; ii < nkids; ii++) { - cerr << "hi-q filter[" << ii << "] " + cerr << progid << " filter[" << ii << "] " << kidpid[ii] << " :; "; for (VS::const_iterator token = filter[ii].cmd.begin(); @@ -736,7 +744,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 << progid << " special kid exited early, status " << WEXITSTATUS(kidstatus) << " with " << alive << " kids still alive" << endl; @@ -745,7 +753,7 @@ bar int sig = WTERMSIG(kidstatus); if (sig == SIGUSR1) {/* normal, no logging required */} else { - cerr << "hi-q: special kid killed by signal " + cerr << progid << " special kid killed by signal " << sig << endl; // this is not normal return(ex_syserr); @@ -795,7 +803,7 @@ bar if (sts == 0){ // should never get here // should be no accounting for blame if there was no blame - cerr << "hi-q: should never happen: no child to blame" << endl; + cerr << progid << " should never happen: no child to blame" << endl; exeunt(ex_syserr); } -- 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 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(-) 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 From 292a76b35fd16cf11613f79ea38693449e3317f6 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 21:07:01 -0700 Subject: separate dnscheck functionality from other features; clean up a little --- tools/libltgrey.c | 136 ++++++++++++++++++++++++++++++++++++++---------------- tools/libltgrey.h | 23 +++------ tools/ltgrey.c | 43 +++++++++++------ 3 files changed, 132 insertions(+), 70 deletions(-) diff --git a/tools/libltgrey.c b/tools/libltgrey.c index d4ec0da..1bf7e9f 100644 --- a/tools/libltgrey.c +++ b/tools/libltgrey.c @@ -73,6 +73,15 @@ void exeunt(const int sts){ #include "utils.h" #include "qq_exit_codes.h" +// constructor +whatsit::whatsit(const std::string name, const std::string _parent_dir) +: parent_dir(_parent_dir), progname(name), mypid(getpid()), + mod_age(0), ac_age(0), + verbosity(0) +{ + gettimeofday(&now, NULL); +} + void whatsit::dump(const string var){ char* str = getenv(var.c_str()); cerr << progname @@ -88,13 +97,14 @@ int whatsit::setup(){ << "[" << mypid << "]"; progid = foo.str(); - ipvar = getenv("TCPREMOTEIP"); - if (ipvar) ipbase = ipvar; - hostvar = getenv("TCPREMOTEHOST"); - if (hostvar) hostname = hostvar; return 0; } +#if 1 +void whatsit::update(const string msg, const timeval new_mod, + const timeval new_ac, const int penalty, const int stain){ +} +#else void whatsit::update(const string msg, const timeval new_mod, const timeval new_ac, const int penalty, const int stain){ if (verbosity){ @@ -128,36 +138,51 @@ void whatsit::update(const string msg, const timeval new_mod, if (utimes(ipname.c_str(), upd)) cerr << "oops" << endl; } +#endif -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); - } - +int whatsit::maybe_mkdir(const string somedir, const string msg){ // see if our directory exists: struct stat dirstat; - int rslt = stat(dirname.c_str(), &dirstat); + int rslt = stat(somedir.c_str(), &dirstat); if (rslt != 0){ if (errno != ENOENT) { - cerr << progid << ": stat failed for '" - << dirname << "' : "; + cerr << progid << " stat failed for " + << msg + << " '" << somedir << "' : "; perror(0); } - rslt = mkdir(dirname.c_str(), 0755); + rslt = mkdir(somedir.c_str(), 0755); if (rslt != 0) { cerr << progid << "uid " << getuid() - << ": mkdir failed for '" - << dirname << "' : "; + << " mkdir failed for " + << msg + << "' " << somedir << "' : "; perror(0); return(ex_syserr); } } + return 0; +} + +#if 1 +int whatsit::doit(const int penalty, const int stain){ + return ex_syserr; +} + +#else +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); + } + + maybe_mkdir(parent_dir, "parent dir"); + maybe_mkdir(parent_dir + "/quarante", "quarantine dir"); + maybe_mkdir(parent_dir + "/repute", "reputation dir"); ipname = dirname + "/" + ipbase; struct stat ipstat; @@ -210,6 +235,7 @@ int whatsit::doit(const int penalty, const int stain){ update("returning customer", mod_orig, now, penalty, stain); return 0; } +#endif typedef vector VU; @@ -260,29 +286,35 @@ int diff(const VU aaa, const VU bbb){ return 0; } -int whatsit::check_dns(){ +int whatsit::check_dns(const char* ipvar, const char* namevar){ + if (!ipvar) { + cerr << progid << " check_dns: no addr specified." << endl; + return ex_syserr; + } 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 + int rslt = check_dns_sub(ipvar, namevar, addr, host, checked); + int sts = rslt; #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; - + if (rslt || verbosity) { + cerr << progid; + if (rslt && !sts) cerr << " (warning)"; + if (rslt) cerr << " DNS inconsistency: "; + else cerr << " DNS OK: "; + cerr << 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){ +int whatsit::check_dns_sub(const char* ipvar, const char* namevar, + string &addr, string &host, vector &checked){ struct addrinfo *result; struct addrinfo *ipresult; @@ -296,6 +328,8 @@ int whatsit::check_dns_sub(string &addr, string &host, vector &checked){ hints.ai_protocol = IPPROTO_TCP; #endif +// convert address-as-string to address-as-bits. +// also get information about family error = getaddrinfo(ipvar, NULL, &hints, &ipresult); if (error == EAI_NONAME) return ex_badDNS; if (error) { // some unexpected error @@ -309,14 +343,38 @@ int whatsit::check_dns_sub(string &addr, string &host, vector &checked){ cerr << "should never happen (addr with no addrs?)" << endl; return ex_syserr; } + +// reconvert from bits to string + family info VUx ipAddr = parse_sockaddr(ipresult->ai_addr); addr = ipAddr.str(); - char* hostvar = getenv("TCPREMOTEHOST"); - if (hostvar) host = hostvar; - else return(ex_badDNS); + if (namevar) { + // inverse lookup already done + host = namevar; + } else { + // namevar not specified; do inverse lookup on our own + + char hostname[NI_MAXHOST] = ""; + char service[NI_MAXHOST] = ""; + +#ifdef convert_bits_to_string + const char* rslt = inet_ntop(ipAddr.fam, &ipAddr.addr[0], + msgbuf, sizeof(msgbuf)); + if (rslt) fprintf(stdout, "%s addrsize: %2d --> ", + msgbuf, addrsize); +#endif + error = getnameinfo(ipresult->ai_addr, ipresult->ai_addrlen, + hostname, NI_MAXHOST, service, NI_MAXHOST, 0); + if (error != 0) { + cerr << progid << " reverse DNS lookup failed: " + << gai_strerror(error) << endl; + return (ex_badDNS); + } + namevar = hostname; // a char*, used below + host = hostname; // a string, returned to caller + } - error = getaddrinfo(hostvar, NULL, &hints, &result); + error = getaddrinfo(namevar, NULL, &hints, &result); if (error == EAI_NONAME) return ex_badDNS; if (error) { cerr << progid diff --git a/tools/libltgrey.h b/tools/libltgrey.h index 585ec01..6f516b4 100644 --- a/tools/libltgrey.h +++ b/tools/libltgrey.h @@ -4,35 +4,26 @@ class whatsit{ public: - std::string dirname; + std::string parent_dir; 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); - } + whatsit(const std::string name, const std::string _parent_dir); 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); + int check_dns(const char* ipvar, const char* namevar); + int check_dns_sub(const char* ipvar, const char* namevar, + std::string &addr, std::string &host, + std::vector &checked); void dump(const std::string var); + int maybe_mkdir(const std::string somedir, const std::string msg); }; diff --git a/tools/ltgrey.c b/tools/ltgrey.c index afdb4c1..c9f384f 100644 --- a/tools/ltgrey.c +++ b/tools/ltgrey.c @@ -14,17 +14,24 @@ string progname; // forward reference: void scan(const string progid, const string p, const int copies=1); + int main(int _argc, char** _argv){ + char* namevar; + std::string hostname; + char* ipvar; + std::string ipbase; + std::string ipname; + mypid = getpid(); int argc(_argc); char** argv(_argv); - const string dirname("/var/qmail/greylist"); - whatsit foo(argv[0], dirname); argc--; argv++; + const string parent_dir("/var/qmail/ltgrey"); + whatsit foo(argv[0], parent_dir); argc--; argv++; int scanmode(0); int copies(1); int penalty(0); int stain(0); - int check(0); + int dnscheck(0); while (argc > 0) { string arg = argv[0]; argc--; argv++; if (prefix(arg, "-scan")) { @@ -33,8 +40,8 @@ int main(int _argc, char** _argv){ copies++; } else if (prefix(arg, "-verbose")) { foo.verbosity++; - } else if (prefix(arg, "-check")) { - check++; + } else if (prefix(arg, "-dnscheck")) { + dnscheck++; } else if (prefix(arg, "-penalize") || prefix(arg, "-penalty")) { if (!argc){ @@ -61,23 +68,29 @@ int main(int _argc, char** _argv){ } if (foo.setup()) return ex_syserr; +// dnscheck mode ... +// Probably a better design would be to +// (a) make more thorough DNS checks, and +// (b) move all the DNS checking to a separate module + + ipvar = getenv("TCPREMOTEIP"); + if (ipvar) ipbase = ipvar; + namevar = getenv("TCPREMOTEHOST"); + if (namevar) hostname = namevar; + + if (dnscheck) { + exeunt(foo.check_dns(ipvar, namevar)); + } + if (scanmode) { + string dirname = parent_dir + "/quarante"; 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 + return sts; - int dns = foo.check_dns(); - if (dns == ex_syserr || dns == ex_spam) return dns; - exeunt(sts); } ////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 67eb4c6c804c728db329a7f4a77d5a3cbd1b993c Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 21:55:41 -0700 Subject: get40 seems to be working --- tools/libltgrey.c | 27 ++++++++++++++++++++++++-- tools/libltgrey.h | 20 ++++++++++++++++--- tools/ltgrey.c | 58 ++++++++++++++++++++++++++++++------------------------- 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/tools/libltgrey.c b/tools/libltgrey.c index 1bf7e9f..38657e3 100644 --- a/tools/libltgrey.c +++ b/tools/libltgrey.c @@ -76,10 +76,12 @@ void exeunt(const int sts){ // constructor whatsit::whatsit(const std::string name, const std::string _parent_dir) : parent_dir(_parent_dir), progname(name), mypid(getpid()), - mod_age(0), ac_age(0), verbosity(0) { - gettimeofday(&now, NULL); +// expand the codes to make some names: +# define foo(name) decode_40[name] = #name; + state_40_macro +# undef foo } void whatsit::dump(const string var){ @@ -399,3 +401,24 @@ int whatsit::check_dns_sub(const char* ipvar, const char* namevar, done: return 0; } + +state_40 whatsit::get40(const string mid){ + string fname = parent_dir + "/quarante/mid_" + mid; + cerr << ".... " << fname << endl; + struct stat mid_stat; + int rslt = stat(fname.c_str(), &mid_stat); + if (rslt != 0) { + if (errno == ENOENT) return unseen; + cerr << progid << " stat: unexpected failure for '" + << fname << "' : "; + perror(0); + return fail; + } + timeval now; + gettimeofday(&now, NULL); + int mod_age = now.tv_sec - mid_stat.st_mtime; +// int ac_age = now.tv_sec - mid_stat.st_atime; + if (mod_age < minimum_age) return young; + if (mod_age < probation) return ripe; + return spoiled; +} diff --git a/tools/libltgrey.h b/tools/libltgrey.h index 6f516b4..1724d53 100644 --- a/tools/libltgrey.h +++ b/tools/libltgrey.h @@ -1,18 +1,31 @@ #include #include /* for gettimeofday(), timeval */ #include +#include + +#define state_40_macro \ +foo(unseen) \ +foo(young) \ +foo(ripe) \ +foo(spoiled) \ +foo(fail) + +// expand the codes to make some names: +#define foo(name) name, +typedef enum { + state_40_macro +} state_40; +#undef foo class whatsit{ public: std::string parent_dir; std::string progname; pid_t mypid; - timeval now; - int mod_age; - int ac_age; std::string suffix; std::string progid; int verbosity; + std::map decode_40; whatsit(const std::string name, const std::string _parent_dir); int doit(const int penalty, const int stain); @@ -26,4 +39,5 @@ public: std::vector &checked); void dump(const std::string var); int maybe_mkdir(const std::string somedir, const std::string msg); + state_40 get40(const std::string mid); }; diff --git a/tools/ltgrey.c b/tools/ltgrey.c index c9f384f..7a63b04 100644 --- a/tools/ltgrey.c +++ b/tools/ltgrey.c @@ -16,10 +16,7 @@ void scan(const string progid, const string p, const int copies=1); int main(int _argc, char** _argv){ - char* namevar; std::string hostname; - char* ipvar; - std::string ipbase; std::string ipname; mypid = getpid(); @@ -29,27 +26,35 @@ int main(int _argc, char** _argv){ whatsit foo(argv[0], parent_dir); argc--; argv++; int scanmode(0); int copies(1); - int penalty(0); + int shift(0); int stain(0); - int dnscheck(0); + int dns_mode(0); + string get40_mid; while (argc > 0) { string arg = argv[0]; argc--; argv++; if (prefix(arg, "-scan")) { scanmode++; - } else if (prefix(arg, "-copy")) { + } else if (prefix(arg, "-copy")) { copies++; - } else if (prefix(arg, "-verbose")) { + } else if (prefix(arg, "-verbose")) { foo.verbosity++; - } else if (prefix(arg, "-dnscheck")) { - dnscheck++; - } else if (prefix(arg, "-penalize") - || prefix(arg, "-penalty")) { + } else if (prefix(arg, "-dns_mode")) { + dns_mode++; + } else if (prefix(arg, "-get40")) { if (!argc){ cerr << "Option '" << arg << "' requires an argument" << endl; exeunt(ex_syserr); } - penalty = atoi(*argv++); argc--; - } else if (prefix(arg, "-stain")) { + get40_mid = *argv++; argc--; + + } else if (prefix(arg, "-shift") + || prefix(arg, "-shift")) { + if (!argc){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + shift = atoi(*argv++); argc--; + } else if (prefix(arg, "-stain")) { if (!argc){ cerr << "Option '" << arg << "' requires an argument" << endl; exeunt(ex_syserr); @@ -68,27 +73,28 @@ int main(int _argc, char** _argv){ } if (foo.setup()) return ex_syserr; -// dnscheck mode ... -// Probably a better design would be to -// (a) make more thorough DNS checks, and -// (b) move all the DNS checking to a separate module - - ipvar = getenv("TCPREMOTEIP"); - if (ipvar) ipbase = ipvar; - namevar = getenv("TCPREMOTEHOST"); - if (namevar) hostname = namevar; - - if (dnscheck) { +// dns_mode mode ... +// Probably it would be better to make more thorough DNS checks. +// + if (dns_mode) { + char* ipvar = getenv("TCPREMOTEIP"); + char* namevar = getenv("TCPREMOTEHOST"); exeunt(foo.check_dns(ipvar, namevar)); } + if (get40_mid.length()){ + state_40 rslt = foo.get40(get40_mid); + cerr << foo.decode_40[rslt] << endl; + return 0; + } + if (scanmode) { string dirname = parent_dir + "/quarante"; scan(foo.progid, dirname, copies); return 0; } - int sts = foo.doit(penalty, stain); + int sts = foo.doit(shift, stain); return sts; } @@ -116,7 +122,7 @@ void scan(const string progid, const string p, const int copies){ gettimeofday(&now, NULL); using namespace boost::filesystem; - if (is_directory(p)) { + 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++) -- cgit v1.2.3 From 2f6fd23b841c1f4e3c18d1cbfd228784aa8c3298 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 22:31:03 -0700 Subject: set40 appears to be working --- tools/libltgrey.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- tools/libltgrey.h | 3 ++- tools/ltgrey.c | 12 +++++++++++- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/tools/libltgrey.c b/tools/libltgrey.c index 38657e3..3a60daa 100644 --- a/tools/libltgrey.c +++ b/tools/libltgrey.c @@ -32,6 +32,11 @@ const int minimum_age(15*minute); const int maximum_age(32*day); const int probation(4*hour); +// beware: access illogically comes *before* modification +// in the array passed to utimes: +const int upd_ac(0); +const int upd_mod(1); + #if 0 void exeunt(const int sts){ if (sts == ex_good) exit(sts); @@ -195,7 +200,7 @@ int whatsit::doit(const int penalty, const int stain){ << ipname << "' : "; perror(0); } - ofstream foo; +//???!! ofstream foo; int fd = creat(ipname.c_str(), 0644); if (fd < 0){ cerr << progid << ": create failed for '" @@ -418,7 +423,45 @@ state_40 whatsit::get40(const string mid){ gettimeofday(&now, NULL); int mod_age = now.tv_sec - mid_stat.st_mtime; // int ac_age = now.tv_sec - mid_stat.st_atime; - if (mod_age < minimum_age) return young; + if (mod_age < minimum_age) return green; if (mod_age < probation) return ripe; return spoiled; } + +int whatsit::set40(const string mid, const int shift){ + string fname = parent_dir + "/quarante/mid_" + mid; + cerr << ".... " << fname << endl; + int fd = creat(fname.c_str(), 0644); + if (fd < 0){ + cerr << progid << ": create failed for '" + << fname << "' : "; + perror(0); + } + if (!shift) { + close(fd); + return 0; + } + + struct stat mid_stat; + int rslt = fstat(fd, &mid_stat); + close(fd); + if (rslt != 0) { + cerr << progid << " fstat: unexpected failure for '" + << fname << "' : "; + perror(0); + return ex_syserr; + } + + timeval now; + gettimeofday(&now, NULL); + timeval upd[2] = { + now, // access + now // modification + }; + upd[upd_mod].tv_sec += shift; + if (utimes(fname.c_str(), upd)){ + cerr << progid << " utimes failed!" << endl; + return ex_syserr; + } + return 0; +} diff --git a/tools/libltgrey.h b/tools/libltgrey.h index 1724d53..cf744d1 100644 --- a/tools/libltgrey.h +++ b/tools/libltgrey.h @@ -5,7 +5,7 @@ #define state_40_macro \ foo(unseen) \ -foo(young) \ +foo(green) \ foo(ripe) \ foo(spoiled) \ foo(fail) @@ -40,4 +40,5 @@ public: void dump(const std::string var); int maybe_mkdir(const std::string somedir, const std::string msg); state_40 get40(const std::string mid); + int set40(const std::string mid, const int shift); }; diff --git a/tools/ltgrey.c b/tools/ltgrey.c index 7a63b04..bb43b80 100644 --- a/tools/ltgrey.c +++ b/tools/ltgrey.c @@ -30,6 +30,7 @@ int main(int _argc, char** _argv){ int stain(0); int dns_mode(0); string get40_mid; + string set40_mid; while (argc > 0) { string arg = argv[0]; argc--; argv++; if (prefix(arg, "-scan")) { @@ -46,7 +47,12 @@ int main(int _argc, char** _argv){ exeunt(ex_syserr); } get40_mid = *argv++; argc--; - + } else if (prefix(arg, "-set40")) { + if (!argc){ + cerr << "Option '" << arg << "' requires an argument" << endl; + exeunt(ex_syserr); + } + set40_mid = *argv++; argc--; } else if (prefix(arg, "-shift") || prefix(arg, "-shift")) { if (!argc){ @@ -88,6 +94,10 @@ int main(int _argc, char** _argv){ return 0; } + if (set40_mid.length()){ + return foo.set40(set40_mid, shift); + } + if (scanmode) { string dirname = parent_dir + "/quarante"; scan(foo.progid, dirname, copies); -- cgit v1.2.3 From f8be4baf5a2318363b42f8883f66ed8a976dfc79 Mon Sep 17 00:00:00 2001 From: John Denker Date: Sun, 29 Jul 2012 23:26:28 -0700 Subject: scan40 appears to be working, much cleaner than last week's version --- tools/libltgrey.c | 63 +++++++++++++++++++++++++++++++++++++-------- tools/libltgrey.h | 7 +++++ tools/ltgrey.c | 77 ++----------------------------------------------------- tools/utils.c | 16 ++++++++++++ tools/utils.h | 1 + 5 files changed, 78 insertions(+), 86 deletions(-) diff --git a/tools/libltgrey.c b/tools/libltgrey.c index 3a60daa..4e92665 100644 --- a/tools/libltgrey.c +++ b/tools/libltgrey.c @@ -32,11 +32,6 @@ const int minimum_age(15*minute); const int maximum_age(32*day); const int probation(4*hour); -// beware: access illogically comes *before* modification -// in the array passed to utimes: -const int upd_ac(0); -const int upd_mod(1); - #if 0 void exeunt(const int sts){ if (sts == ex_good) exit(sts); @@ -408,8 +403,16 @@ done: } state_40 whatsit::get40(const string mid){ - string fname = parent_dir + "/quarante/mid_" + mid; - cerr << ".... " << fname << endl; + timeval junk[2]; + return get40(mid, junk); +} + +state_40 whatsit::get40(const string mid, timeval times[2]){ + timeval now; + gettimeofday(&now, NULL); + times[0] = times[1] = now; // in case of early return + string fname = parent_dir + "/quarante/mid_" + purify(mid); + //xxx cerr << ".... " << fname << endl; struct stat mid_stat; int rslt = stat(fname.c_str(), &mid_stat); if (rslt != 0) { @@ -419,8 +422,10 @@ state_40 whatsit::get40(const string mid){ perror(0); return fail; } - timeval now; - gettimeofday(&now, NULL); + times[upd_ac ].tv_sec = mid_stat.st_atime; + times[upd_ac ].tv_usec = 0; + times[upd_mod].tv_sec = mid_stat.st_mtime; + times[upd_mod].tv_usec = 0; int mod_age = now.tv_sec - mid_stat.st_mtime; // int ac_age = now.tv_sec - mid_stat.st_atime; if (mod_age < minimum_age) return green; @@ -429,8 +434,8 @@ state_40 whatsit::get40(const string mid){ } int whatsit::set40(const string mid, const int shift){ - string fname = parent_dir + "/quarante/mid_" + mid; - cerr << ".... " << fname << endl; + string fname = parent_dir + "/quarante/mid_" + purify(mid); + //xxx cerr << ".... " << fname << endl; int fd = creat(fname.c_str(), 0644); if (fd < 0){ cerr << progid << ": create failed for '" @@ -465,3 +470,39 @@ int whatsit::set40(const string mid, const int shift){ } return 0; } + +void whatsit::scan40(const int copies){ + timeval now; + gettimeofday(&now, NULL); + using namespace boost::filesystem; + + string dir40 = parent_dir + "/quarante"; + + if (is_directory(dir40)) { + for (directory_iterator itr(dir40); itr!=directory_iterator(); ++itr) { + string basename = itr->path().filename(); + if (is_regular_file(itr->status())) { + string tag("mid_"); + if (prefix(tag, basename)) { + for (int ii = 0; ii < copies; ii++) + cout << setw(20) << left << basename; // display filename only + + timeval times[2]; + state_40 rslt = get40(basename.substr(tag.length()), times); + int mod_age = now.tv_sec - times[upd_mod].tv_sec; + int ac_age = now.tv_sec - times[upd_ac ].tv_sec; + cout << setw(10) << right << time_out(mod_age) + << " " << setw(10) << right << time_out(ac_age); + cout << " " << decode_40[rslt]; + cout << endl; + } else { + /* filename does not begin with tag "mid_" */ + } + } + } + } + else { + // starting point is not a directory: + cout << (exists(dir40) ? "Found: " : "Not found: ") << dir40 << '\n'; + } +} diff --git a/tools/libltgrey.h b/tools/libltgrey.h index cf744d1..14ed144 100644 --- a/tools/libltgrey.h +++ b/tools/libltgrey.h @@ -3,6 +3,11 @@ #include #include +// beware: access illogically comes *before* modification +// in the array passed to utimes: +const int upd_ac(0); +const int upd_mod(1); + #define state_40_macro \ foo(unseen) \ foo(green) \ @@ -40,5 +45,7 @@ public: void dump(const std::string var); int maybe_mkdir(const std::string somedir, const std::string msg); state_40 get40(const std::string mid); + state_40 get40(const std::string mid, timeval times[2]); int set40(const std::string mid, const int shift); + void scan40(const int copies); }; diff --git a/tools/ltgrey.c b/tools/ltgrey.c index bb43b80..1494338 100644 --- a/tools/ltgrey.c +++ b/tools/ltgrey.c @@ -33,7 +33,7 @@ int main(int _argc, char** _argv){ string set40_mid; while (argc > 0) { string arg = argv[0]; argc--; argv++; - if (prefix(arg, "-scan")) { + if (prefix(arg, "-scan40")) { scanmode++; } else if (prefix(arg, "-copy")) { copies++; @@ -99,8 +99,7 @@ int main(int _argc, char** _argv){ } if (scanmode) { - string dirname = parent_dir + "/quarante"; - scan(foo.progid, dirname, copies); + foo.scan40(copies); return 0; } @@ -108,75 +107,3 @@ int main(int _argc, char** _argv){ return 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/utils.c b/tools/utils.c index aecbfda..c544f20 100644 --- a/tools/utils.c +++ b/tools/utils.c @@ -3,6 +3,7 @@ #include //#include /* for abs() */ #include +#include /* for isalnum() */ // strip off the directory part of a path, leaving just // the basic filename @@ -58,3 +59,18 @@ std::string ltrim(const std::string foo){ if (where == foo.npos) return foo; return foo.substr(where); } + +static const std::string Pure_Enough("+-_.,@%~"); + +std::string purify(const std::string arg){ + using namespace std; + string rslt(arg); + for (string::iterator ptr = rslt.begin(); + ptr != rslt.end(); ptr++){ + char ch = *ptr; + if (isalnum(ch)) continue; + if (Pure_Enough.find(ch) != string::npos) continue; + *ptr = '~'; + } + return rslt; +} diff --git a/tools/utils.h b/tools/utils.h index ec467c6..cbcb795 100644 --- a/tools/utils.h +++ b/tools/utils.h @@ -4,3 +4,4 @@ std::string time_out(const int _ttt); std::string toLower(const std::string a); std::string ltrim(const std::string a); +std::string purify(const std::string arg); -- cgit v1.2.3