These programs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
These programs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with these programs; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Mail suggestions and bug reports for these programs to
"Masahiko Ito" <m-ito@myh.no-ip.org>
I'm very inspired from ` The Selective SMTP Rejection (S25R) System by Mr. ASAMI Hideo'. It's very simple and light weight way for filtering spam. There is operative example with postfix and qmail in S25R's page. but, unfortunately there is no operative example with sendmail !
It's hard to use MILTER with sendmail for me, so I decided to develop this :-)
Usage : smtp_wrapper [-mh hostname] [-mp port] [-q backlog] [-sh smtpserver_hostname] [-sp smtpserver_port] [-t timeout_sec] [-d delay_sec] [-if ip_filter] [-f contents_filter] [-cm child_max] [-i minimum_interval_sec] [-F] -mh hostname : my hostname [ANY] -mp port : my port [25] -q backlog : socket queue number [5] -sh smtpserver_hostname : real smtp hostname [localhost] -sp smtpserver_port : real smtp port [8025] -t timeout_sec : timeout second [no timeout] -d delay_sec : delay second for initial connection [0] -if ip_filter : filter program for IP check [/usr/local/smtp_wrapper/smtp_ip_filter] -f contents_filter : filter program for contents check [/usr/local/smtp_wrapper/smtp_contents_filter] -cm child_max : max number of connection to real smtp daemon [10] -i minimum_interval_sec : minimum interval second of connection from same ip address [0] -nsc : no sequence error check -F : run in foreground
DAEMON_OPTIONS(`Port=8025, Name=MTA')dnl
DAEMON_OPTIONS(`Port=8025, Name=NoMTA, Addr=127.0.0.1, M=E')dnl
if [ -x /usr/local/smtp_wrapper/smtp_wrapper ] then /usr/local/smtp_wrapper/smtp_wrapper \ -t 60 \ -d 10 \ -if /usr/local/smtp_wrapper/smtp_filter1.sh \ -f /usr/local/smtp_wrapper/smtp_filter2.sh \ -cm 10 \ -i 5 echo ' smtp_wrapper' fi
+-----------------------------------------------------------+ | | | | +-----+ command +------------------+ command +----------------+ | | |--------->|(1) [2]|----------->| | | | MTA |SOCKET(25)| smtp_wrapper |SOCKET(8025)| MTA | | |(MUA)|<---------|[1] (2)|<-----------| (sendmail etc) | | | | response | | response +----------------+ | +-----+ | | (3) [3] | | | +------+----+------+ | | A P | | | | I | | | | P | | | | E | | | (stdout) | V (stdin) | | +--------+ | | | IP |--------+ | | | FILTER |CONTENTS| | | | | FILTER | | | +--------+ | | | +---------+ | | | +-----------------------------------------------------------+
450 This message was rejected according to site policy(rejected by ip_filter. IP=%s)(SW02)\r\n
450 This message was rejected according to site policy(rejected by ip_filter. IP=%s)(SW02)\r\n
450 This message was rejected according to site policy(rejected by seq error. IP=%s)(SW05)\r\n
450 This message was accepted partly, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW09)\r\n 450 This message was accepted all, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW10)\r\n
#! /bin/ash # # This part of smtp_wrapper is distributed under GNU General Public License. # # Sample spam filter script # by "Masahiko Ito" <m-ito@myh.no-ip.org> # # Thanks to http://www.gabacho-net.jp/anti-spam/anti-spam-system.html # # S25R check script # #============================================================ # # signal trap # trap "spam_exit 1 \"kill by signal\"" INT HUP TERM QUIT #============================================================ # # init PATH # PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" export PATH #============================================================ # # smtp_wrapper directory # smtp_wrapper_dir="/usr/local/smtp_wrapper" # # temporary spool directosr tmp="/var/tmp" # # syslog ID sl_ident="smtp_filter1" # # right host's IP # # ex. ^127\.0\.0\.1$ # ^192\.168\.0\.[0-9]*$ # white_ip_db="${smtp_wrapper_dir}/white_ip_db" # # right FQDN # # ex. ^myh\.no-ip\.org$ # white_hostname_db="${smtp_wrapper_dir}/white_hostname_db" # # unright host's IP # # ex. ^172\.21\.0\.123$ # black_ip_db="${smtp_wrapper_dir}/black_ip_db" # # unright FQDN # # ex. ^mail\.spammer\.org$ # black_hostname_db="${smtp_wrapper_dir}/black_hostname_db" # #============================================================ # # constants # cr=`echo -n -e '\r'`; export cr tab=`echo -n -e '\t'`; export tab pid="$$"; export pid #============================================================ # # exit by spam # spam_exit () { logger -p mail.info -t ${sl_ident} "[$$]:IP=${from_ip}" logger -p mail.info -t ${sl_ident} "[$$]:HOST=${from_hostname}" logger -p mail.info -t ${sl_ident} "[$$]:EXIT=$1" logger -p mail.info -t ${sl_ident} "[$$]:REASON=$2" #------------------------------------------------------------ echo "[$$]:IP=${from_ip} HOST=${from_hostname} EXIT=$1 REASON=$2" # # You may use `sleep' for S25R+tarpitting(http://d.hatena.ne.jp/stealthinu/20060706/p5) # instead of `echo' # # sleep 60s #------------------------------------------------------------ rm -f ${tmp}/smtp_filter1.*.$$.tmp exit $1 } #============================================================ # # spam check by S25R # spam_check () { if [ "X${from_ip}" = "X" ] then spam_exit 2 "unknown ip" # *** exit *** fi # for i in `cat ${black_ip_db} |\ egrep -v '^#'` do black_ip=`echo "${from_ip}" |\ egrep -i "$i"` if [ "X${black_ip}" != "X" ] then spam_exit 3 "black IP" # *** exit *** fi done # for i in `cat ${white_ip_db} |\ egrep -v '^#'` do white_ip=`echo "${from_ip}" |\ egrep -i "$i"` if [ "X${white_ip}" != "X" ] then break; fi done # from_hostname=`host ${from_ip} 2>/dev/null |\ egrep -i 'domain name pointer' |\ head -1 |\ sed -e 's/^.* domain name pointer *//;s/\.$//'` if [ "X${white_ip}" = "X" -a "X${from_hostname}" = "X" ] then spam_exit 4 "no dns" # *** exit *** fi # for i in `cat ${black_hostname_db} |\ egrep -v '^#'` do black_hostname=`echo "${from_hostname}" |\ egrep -i "$i"` if [ "X${black_hostname}" != "X" ] then spam_exit 5 "black hostname" # *** exit *** fi done # for i in `cat ${white_hostname_db} |\ egrep -v '^#'` do white_hostname=`echo "${from_hostname}" |\ egrep -i "$i"` if [ "X${white_hostname}" != "X" ] then break; fi done # if [ "X${white_ip}" = "X" -a "X${white_hostname}" = "X" ] then # spam_host=`echo ${from_hostname} |\ egrep -i '^[^\.]*[0-9][^0-9\.]+[0-9]'` if [ "X${spam_host}" != "X" ] then spam_exit 6 "rule 1" # *** exit S25R rule 1 *** fi # spam_host=`echo ${from_hostname} |\ egrep -i '^[^\.]*[0-9]{5}'` if [ "X${spam_host}" != "X" ] then spam_exit 7 "rule 2" # *** exit S25R rule 2 *** fi # spam_host=`echo ${from_hostname} |\ egrep -i '^([^\.]+\.)?[0-9][^\.]*\.[^\.]+\..+\.[a-z]'` if [ "X${spam_host}" != "X" ] then spam_exit 8 "rule 3" # *** exit S25R rule 3 *** fi # spam_host=`echo ${from_hostname} |\ egrep -i '^[^\.]*[0-9]\.[^\.]*[0-9]-[0-9]'` if [ "X${spam_host}" != "X" ] then spam_exit 9 "rule 4" # *** exit S25R rule 4 *** fi # spam_host=`echo ${from_hostname} |\ egrep -i '^[^\.]*[0-9]\.[^\.]*[0-9]\.[^\.]+\..+\.'` if [ "X${spam_host}" != "X" ] then spam_exit 10 "rule 5" # *** exit S25R rule 5 *** fi # spam_host=`echo ${from_hostname} |\ egrep -i '^(dhcp|dialup|ppp|adsl)[^\.]*[0-9]'` if [ "X${spam_host}" != "X" ] then spam_exit 11 "rule 6" # *** exit S25R rule 6 *** fi fi } #============================================================ # # Main proc # from_ip="" from_hostname="" white_ip="" white_hostname="" black_ip="" black_hostname="" # #============================================================ # # S25R check # from_ip=${SW_FROM_IP} # spam_check #============================================================ # # terminate and exit # rm -f ${tmp}/smtp_filter1.*.$$.tmp exit 0
#! /bin/ash # # This part of smtp_wrapper is distributed under GNU General Public License. # # Sample spam filter script # by "Masahiko Ito" <m-ito@myh.no-ip.org> # # Thanks to http://www.gabacho-net.jp/anti-spam/anti-spam-system.html # # contents check(anonymous relay check) # #============================================================ # # signal trap # trap "spam_exit 1 \"kill by signal\"" INT HUP TERM QUIT #============================================================ # # init PATH # PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" export PATH #============================================================ # # smtp_wrapper directory # smtp_wrapper_dir="/usr/local/smtp_wrapper" # # temporary spool directosr tmp="/var/tmp" # # syslog ID sl_ident="smtp_filter2" # # relay allow IP # # ex. ^127\.0\.0\.1$ # ^192\.168\.0\.[0-9]*$ # relay_allow_host="${smtp_wrapper_dir}/relay_allow_host_db" # # allow part of address for outer MTA # # ex. myh.no-ip.org # relay_allow_address="${smtp_wrapper_dir}/relay_allow_address_db" #============================================================ # # constant # cr=`echo -n -e '\r'`; export cr tab=`echo -n -e '\t'`; export tab pid="$$"; export pid #============================================================ # # spam exit # spam_exit () { if [ -r ${tmp}/smtp_filter2.*.$$.tmp ] then logger -p mail.info -t ${sl_ident} "[$$]:`cat ${tmp}/smtp_filter2.*.$$.tmp | head -50 | egrep -i '^MAIL *FROM:|^RCPT *TO:|^From:|^To:|^Subject:|^Date:'`" else logger -p mail.info -t ${sl_ident} "[$$]:`cat | head -50 | egrep -i '^MAIL *FROM:|^RCPT *TO:|^From:|^To:|^Subject:|^Date:'`" fi logger -p mail.info -t ${sl_ident} "[$$]:IP=${from_ip}" logger -p mail.info -t ${sl_ident} "[$$]:HOST=${from_hostname}" logger -p mail.info -t ${sl_ident} "[$$]:EXIT=$1" logger -p mail.info -t ${sl_ident} "[$$]:REASON=$2" rm -f ${tmp}/smtp_filter2.*.$$.tmp echo "[$$]:IP=${from_ip} HOST=${from_hostname} EXIT=$1 REASON=$2" exit $1 } #============================================================ # # anonymous relay check # relay_check () { allow_ip="" for i in `cat ${relay_allow_host}|\ sed -e "s/${tab}/ /g;s/ */ /g" |\ cut -d ' ' -f 1` do allow_ip=`echo ${from_ip} |\ egrep "$i"` if [ "X${allow_ip}" != "X" ] then break fi done # if [ "X${allow_ip}" = "X" ] then for i in `cat ${tmp}/smtp_filter2.1.$$.tmp |\ awk 'BEGIN{ cr = ENVIRON["cr"] } { if (length($0) == 1 && index($0, cr) > 0){ exit; }else{ print; } }' |\ egrep -i '^RCPT *TO:' |\ sed -e 's/ *//g` do for j in `cat ${relay_allow_address}` do allow_rcpt="" allow_rcpt=`echo $i |\ egrep "$j"` if [ "X${allow_rcpt}" != "X" ] then break; fi done if [ "X${allow_rcpt}" = "X" ] then spam_exit 12 "no relay" # *** exit *** fi done fi } #============================================================ # # Main proc # from_ip="" from_hostname="" # #============================================================ # # spool all messages into temporary file # cat >${tmp}/smtp_filter2.1.$$.tmp #============================================================ # # anonymous relay check # from_ip=${SW_FROM_IP} from_hostname=`host ${from_ip} 2>/dev/null |\ egrep -i 'domain name pointer' |\ head -1 |\ sed -e 's/^.* domain name pointer *//;s/\.$//'` # relay_check # #============================================================ # # If right messages, output it # export from_ip export from_hostname # cat ${tmp}/smtp_filter2.1.$$.tmp |\ awk 'BEGIN{ from_ip = ENVIRON["from_ip"] from_hostname = ENVIRON["from_hostname"] pid = ENVIRON["pid"] out_sw = 0; } { if (out_sw == 0){ head = toupper($0); if (head ~ /^DATA\r$/ || head ~ /^DATA$/){ out_sw = 1; } } if (out_sw == 1){ print $0; } } END{ if (out_sw == 0){ printf("[%d]:IP=%-s HOST=%-s REASON=NOT spam, but NO DATA\n", pid, from_ip, from_hostname) } }' # #============================================================ # # terminate and exit # rm -f ${tmp}/smtp_filter2.*.$$.tmp exit 0
fetchmail -S localhost/8025 ...and pass through smtp_wrapper is better.