--- spamass-milter.h.orig 2002-07-22 23:02:12.000000000 -0300 +++ spamass-milter.h 2002-10-31 11:35:36.000000000 -0300 @@ -30,6 +30,7 @@ string retrieve_field(const string&, const string&); +sfsistat mlfi_connect(SMFICTX*, char*, _SOCK_ADDR *); sfsistat mlfi_envfrom(SMFICTX*, char**); sfsistat mlfi_header(SMFICTX*, char*, char*); sfsistat mlfi_eoh(SMFICTX*); --- spamass-milter.cpp.orig 2002-08-14 13:15:10.000000000 -0300 +++ spamass-milter.cpp 2003-01-02 10:40:27.000000000 -0200 @@ -80,6 +80,7 @@ #include #include #include +#include // C++ includes #include @@ -88,6 +89,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -109,7 +111,7 @@ "SpamAssassin", // filter name SMFI_VERSION, // version code -- leave untouched SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY, // flags - NULL, // info filter callback + NULL, // connect info filter callback NULL, // HELO filter callback mlfi_envfrom, // envelope filter callback NULL, // envelope recipient filter callback @@ -121,7 +123,94 @@ mlfi_close, // connection cleanup callback }; +static vector accepted_domains; +typedef pair masked_ip; +static vector accepted_networks; + +static void tolower(string& s) +{ + for (int i=0; i < s.size(); ++i) { + s[i] = tolower(s[i]); + } +} + +static void accept_relay(const char *relay_) +{ + string relay(relay_); + string::size_type noquad = relay.find_first_not_of("0123456789./"); + + if (noquad != string::npos) { // domain name + tolower(relay); + accepted_domains.push_back(relay); + } else { + string::size_type slash = relay.find_first_of('/'); + unsigned long mask = 0xffffffff; + if (slash != string::npos) { // net/mask + mask = inet_addr(relay.c_str() + slash + 1); + if (mask == INADDR_NONE) { + cerr << "error: Invalid netmask in relay spec (" << relay_ << ")."; + exit(1); + } + relay.erase(slash); + } + unsigned long net = inet_addr(relay.c_str()); + if (net == INADDR_NONE) { + cerr << "error: Invalid network in relay spec (" << relay_ << ")."; + exit(1); + } + + accepted_networks.push_back(make_pair(net&mask, mask)); + } +} + +static void dump_accepted_relays() +{ + for (vector::iterator i = accepted_domains.begin(); + i != accepted_domains.end(); ++i) { + debug(2, "accept_relay(%s)", i->c_str()); + } + for (vector::iterator i = accepted_networks.begin(); + i != accepted_networks.end(); ++i) { + debug(2, "accept_relay(%08lx, %08lx)", ntohl(i->first), ntohl(i->second)); + } +} + +static bool is_accepted_relay(char *hostname, _SOCK_ADDR *hostaddr) +{ + if (hostname && accepted_domains.size()) { + string name(hostname); + tolower(name); + for (vector::iterator i = accepted_domains.begin(); + i != accepted_domains.end(); ++i) { + if (name.size() >= i->size() + && name.substr(name.size() - i->size(), i->size()) == *i) { + debug(3, "accept_relay(%s)", hostname); + return true; + } + } + debug(3, "dont accept_relay(%s)", hostname); + } + + if (hostaddr + && hostaddr->sa_family == AF_INET + && accepted_networks.size()) { + unsigned long net = reinterpret_cast(hostaddr)->sin_addr.s_addr; + for (vector::iterator i = accepted_networks.begin(); + i != accepted_networks.end(); ++i) { + if ((net & i->second) == i->first) { + debug(3, "accept_relay(%08lx)", ntohl(net)); + return true; + } + } + debug(3, "dont accept_relay(%08lx)", ntohl(net)); + } + + return false; +} + int flag_debug=0; +bool flag_body=true; +bool flag_accept=false; // {{{ main() @@ -129,13 +218,20 @@ main(int argc, char* argv[]) { int c, err = 0; - const char *args = "p:fd:"; + const char *args = "A:Bp:fd:"; char *sock = NULL; bool dofork = false; /* Process command line options */ while ((c = getopt(argc, argv, args)) != -1) { switch (c) { + case 'A': + flag_accept = true; + accept_relay(optarg); + break; + case 'B': + flag_body = false; + break; case 'p': sock = strdup(optarg); break; @@ -154,10 +250,12 @@ if (!sock || err) { cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl; cout << "SpamAssassin Sendmail Milter Plugin" << endl; - cout << "Usage: spamass-milter -p socket [-f] [-d nn]" << endl; + cout << "Usage: spamass-milter -p socket [-A relay] [-B] [-f] [-d nn]" << endl; cout << " -p socket: path to create socket" << endl; + cout << " -A: accept relay by domain name or n.n.n.n/m.m.m.m network spec" << endl; + cout << " -B: don't change message body" << endl; cout << " -f: fork into background" << endl; - cout << " -d nn: set debug level to nn (1 or 2). Logs to syslog" << endl; + cout << " -d nn: set debug level to nn (1 to 3). Logs to syslog" << endl; exit(EX_USAGE); } @@ -180,6 +278,14 @@ openlog("spamass-milter", LOG_PID, LOG_MAIL); + if (!flag_body) + smfilter.xxfi_flags &= ~SMFIF_CHGBODY; + + if (flag_accept) { + smfilter.xxfi_connect = mlfi_connect; + dump_accepted_relays(); + } + (void) smfi_setconn(sock); if (smfi_register(smfilter) == MI_FAILURE) { fprintf(stderr, "smfi_register failed\n"); @@ -318,7 +424,7 @@ if (old > 0 && newstring != oldstring) smfi_chgheader(ctx,"Subject",1,newstring.size() > 0 ? const_cast(newstring.c_str()) : NULL ); - else if (newstring.size()>0) + else if (old == 0 && newstring.size()>0) smfi_addheader(ctx, "Subject", const_cast(newstring.c_str())); @@ -334,18 +440,19 @@ if (old > 0 && newstring != oldstring) smfi_chgheader(ctx,"Content-Type",1,newstring.size() > 0 ? const_cast(newstring.c_str()) : NULL ); - else if (assassin->content_type().size()>0) + else if (old == 0 && assassin->content_type().size()>0) smfi_addheader(ctx, "Content-Type", const_cast(newstring.c_str())); // Replace body with the one SpamAssassin provided // - string::size_type body_size = assassin->d().size() - bob; - unsigned char* bodyp = (unsigned char*) - const_cast(assassin->d().substr(bob, string::npos).c_str()); - if ( smfi_replacebody(ctx, bodyp, body_size) == MI_FAILURE ) - throw string("error. could not replace body."); - + if (flag_body) { + string::size_type body_size = assassin->d().size() - bob; + unsigned char* bodyp = (unsigned char*) + const_cast(assassin->d().substr(bob, string::npos).c_str()); + if ( smfi_replacebody(ctx, bodyp, body_size) == MI_FAILURE ) + throw string("error. could not replace body."); + } }; // erase mail right away @@ -387,6 +494,25 @@ // {{{ MLFI callbacks // +// Gets called at the start of a SMTP connection +// +// checks if the messages in this connection should be accepted +// +sfsistat +mlfi_connect(SMFICTX* ctx, char* hostname, _SOCK_ADDR *hostaddr) +{ + debug(1, "mlfi_connect: enter"); + + if (is_accepted_relay(hostname, hostaddr)) { + debug(1, "mlfi_connect: accept"); + return SMFIS_ACCEPT; + } else { + debug(1, "mlfi_connect: continue"); + return SMFIS_CONTINUE; + } +} + +// // Gets called first for all messages // // creates SpamAssassin object and makes pointer to it