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