#!/bin/sh ########################### ## Creates greylist entry for Spamcop's top offending /24 (CIDR) IP blocks. ## Usage: greylist-spamcop-offenders [-n] [NUMBER] ## -n Show what would be done without modifying the config file. ## (This lets you paste the results if you don't want automation.) ## NUMBER Number of top offending /24 CIDR blocks to list (default=12) ## ## Edit greylist.conf with an entry like: ## racl greylist list "spamcop bad 24-blocks" delay 45m autowhite 2d ## ## Run this from /etc/cron.daily (or root's crontab) for optimal usage. ## Source: http://spamcop.net/w3m?action=map;net=cmaxcnt;mask=65535;sort=spamcnt ## ## greylist-spamcop-offenders 0.4 (c) 2009 Adam Katz , AGPL3 ########################### #### This program is free software: you can redistribute it and/or modify #### it under the terms of the GNU Affero General Public License as #### published by the Free Software Foundation, either version 3 of the #### License, or (at your option) any later version. #### #### This program 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 Affero General Public License at . #### #### Explicit permission is given to Emmanuel Dreyfus to include this code in #### the official milter-greylist with the BSD license. ########################### ### Depends on greylist-milter, GNU sed (probably), and POSIX-compliant /bin/sh ### ### Changelog: ### 0.4, 2009-03-28: up to spec on license ### 0.3, 2008-04-23: restart the milter when done (not needed?) ### 0.2, 2008-04-16: lots of hand-holding options ########################### prog=`which $0 2>/dev/null||echo $0` conf=`ls -t /etc/greylist.conf /etc/milter-greylist/greylist.conf /etc/mail/greylist.conf 2>/dev/null |head -n1` # find config file if ! [ -s "$conf" ]; then echo "Can't find/read greylist.conf. Please install milter-greylist." >&2 exit 1 fi tmp=`tempfile 2>/dev/null || mktemp 2>/dev/null || echo /tmp/tmp.gso.$$` trap "rm -f $tmp" 0 1 2 5 15 spam_ip_blocks='http://spamcop.net/w3m?action=map;net=cmaxcnt;mask=65535;sort=spamcnt;format=text' unset TEST NUM_TOP_OFFENDERS while [ -n "$1" ]; do if [ "x$1" = "x-n" ] then TEST=true elif [ "$1" -ge 1 ] 2>/dev/null # "not a number" errors return false then NUM_TOP_OFFENDERS="$1" elif echo "x$1" |grep "x--*h" >/dev/null # --help is parsed here then sed -e '/^## /!d;s///' -e "s:greylist.conf:$conf:" "$prog"; exit 0 else echo "${0%%*/}: Skipping unknown argument '$1'" >&2 fi shift done start_regex='^list .spamcop bad 24-blocks' START=`sed -n "/$start_regex/{=;q}" $conf` [ -n "$START" ] && END=`sed -n "$START,/}/=" $conf |tail -n1` # '}' after $START TOTAL=`sed -n '$=' $conf` # If there is no prior entry, we stick it in front of the first list if [ -z "$END" ] || [ "$END" -ge "$TOTAL" ]; then NEW=true START=`sed -n "n;/^list /{=;q}" $conf` # skip line 1 (can't subtract 1) # Try to back up over comments and blanks # before $START; /find non-comments/ {next line; show line num} |get last one NON_COMMENT=`sed -n "1,$(($START-1))!d;/^[^#]/{n;=}" $conf |tail -n1` [ "$NON_COMMENT" -gt 1 ] 2>/dev/null && START="$NON_COMMENT" # We use "lines 0 to (start-1)" + "offenders" + "lines (end+1) to end" ... # So if the first list is start=4, we want start=4 and end=3 # so that we build "lines 0 to 3" + "offenders" + "lines 4 to end" END=$(($START-1)) fi if [ -n "$TEST" ]; then echo '"spamcop bad 24-blocks" is currently:' if [ "$START" -ge "$END" ] # the hack for no prior entry in the "if" above then echo ' (not present in config)' else sed -e "$START,$END!d" -e 's/^/ /' $conf fi echo '' fi ( sed "1,$(($START-1))!d" $conf # conf before this section [ -n "$NEW" ] && echo '' echo 'list "spamcop bad 24-blocks" addr { \ # DO NOT EDIT THIS SECTION!' unset line i for block in `wget -qq -O - "$spam_ip_blocks" 2>/dev/null \ |sed -e "/\/24\t/!d" -e "s/\t.*//g" -e $((1+${NUM_TOP_OFFENDERS:-12}))q` do while [ ${#block} -lt 16 ]; do block=" $block"; done # spacing for columns line="$line$block " i=$((${i:-1}+1)) # increment (to 2 if this is a new row) [ $i -gt 4 ] && echo " $line \\" && unset line i done [ -n "$i" ] && echo " $line \\" echo "} # updated by $prog on `date +%Y-%m-%d`" [ -n "$NEW" ] && echo '' sed "$(($END+1)),\$!d" $conf # conf after this section ) > $tmp touch --reference $conf $tmp 2>/dev/null # prep date sync (see touch line below) if [ -n "$TEST" ] then echo '"spamcop bad 24-blocks" would become:' sed -e "/$start_regex/,/\}/!d" -e 's/^/ /' $tmp echo -n "View proposed configuration file ($tmp) [yN]? " read YN [ "$YN" = "${YN#[Yy]}" ] || less $tmp echo "Discarded changes. (Run it without '-n' to make it write)." elif ! diff -sq $tmp $conf >/dev/null 2>&1; then # only do stuff if new data if cp $tmp $conf 2>/dev/null; then # no reminder when the copy fails touch --reference $tmp $conf 2>/dev/null # timestamp = manual update [ -n "$NEW" ] && echo "Don't forget to activate it! (See '$0 --help')" else echo "${0##*/}: ERROR: Could not write to '$conf'" >&2; exit 1 fi /etc/init.d/milter-greylist reload >/dev/null 2>&1 fi exit 0