2016-12-20 03:11:25 +08:00
#!/bin/sh
# dns based ad/abuse domain blocking
# written by Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the GNU General Public License v3.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# set initial defaults
#
LC_ALL = C
PATH = "/usr/sbin:/usr/bin:/sbin:/bin"
2017-01-24 02:03:41 +08:00
adb_ver = "2.1.5"
2016-12-20 03:11:25 +08:00
adb_enabled = 1
adb_debug = 0
adb_whitelist = "/etc/adblock/adblock.whitelist"
adb_whitelist_rset = "\$1 ~/^([A-Za-z0-9_-]+\.){1,}[A-Za-z]+/{print tolower(\"^\"\$1\"\\\|[.]\"\$1)}"
adb_fetch = "/usr/bin/wget"
adb_fetchparm = "--no-config --quiet --tries=1 --no-cache --no-cookies --max-redirect=0 --timeout=5 --no-check-certificate -O"
# f_envload: load adblock environment
#
f_envload( )
{
# source in system library
#
if [ -r "/lib/functions.sh" ]
then
. "/lib/functions.sh"
else
f_log "error" "status ::: system library not found"
fi
2017-01-24 02:03:41 +08:00
# set dns backend environment
2017-01-05 17:25:02 +08:00
#
adb_dns = " $( uci -q get adblock.global.adb_dns) "
if [ " ${ adb_dns } " = "unbound" ]
then
adb_dnsdir = "/tmp/lib/unbound"
adb_dnsformat = "awk '{print \"local-zone: \042\"\$0\"\042 static\"}'"
else
adb_dns = "dnsmasq"
adb_dnsdir = "/tmp/dnsmasq.d"
adb_dnsformat = "awk '{print \"local=/\"\$0\"/\"}'"
fi
adb_dnshidedir = " ${ adb_dnsdir } /.adb_hidden "
adb_dnsprefix = "adb_list"
2016-12-20 03:11:25 +08:00
# parse global section by callback
#
config_cb( )
{
local type = " ${ 1 } "
if [ " ${ type } " = "adblock" ]
then
option_cb( )
{
local option = " ${ 1 } "
local value = " ${ 2 } "
eval " ${ option } =\" ${ value } \" "
}
else
reset_cb
fi
}
# parse 'service' and 'source' sections
#
parse_config( )
{
local value opt section = " ${ 1 } " options = "enabled adb_dir adb_src adb_src_rset adb_src_cat"
if [ " ${ section } " != "backup" ]
then
eval " adb_sources=\" ${ adb_sources } ${ section } \" "
fi
for opt in ${ options }
do
config_get value " ${ section } " " ${ opt } "
if [ -n " ${ value } " ]
then
eval " ${ opt } _ ${ section } =\" ${ value } \" "
fi
done
}
# load adblock config
#
config_load adblock
config_foreach parse_config service
config_foreach parse_config source
}
# f_envcheck: check/set environment prerequisites
#
f_envcheck( )
{
# check 'enabled' option
#
2016-12-31 21:17:40 +08:00
if [ ${ adb_enabled } -ne 1 ]
2016-12-20 03:11:25 +08:00
then
2016-12-23 02:33:59 +08:00
if [ " $( ls -dA " ${ adb_dnsdir } / ${ adb_dnsprefix } " * >/dev/null 2>& 1) " ]
then
f_rmdns
f_dnsrestart
fi
2016-12-20 03:11:25 +08:00
f_log "info " "status ::: adblock is currently disabled, please set adb_enabled to '1' to use this service"
exit 0
fi
# check fetch utility
#
2017-01-24 02:03:41 +08:00
if [ -z " ${ adb_fetch } " ] || [ -z " ${ adb_fetchparm } " ] || [ ! -f " ${ adb_fetch } " ] || [ " $( readlink -fn " ${ adb_fetch } " ) " = "/bin/busybox" ]
2016-12-20 03:11:25 +08:00
then
2017-01-24 02:03:41 +08:00
f_log "error" "status ::: required download utility with ssl support not found, e.g. install full 'wget' package"
fi
if [ " ${ adb_fetch } " = "/usr/bin/wget" ] && [ " $( readlink -fn " ${ adb_fetch } " ) " = "/bin/uclient-fetch" ]
then
adb_fetch = "/bin/uclient-fetch"
adb_fetchparm = "-q --timeout=5 --no-check-certificate -O"
2016-12-20 03:11:25 +08:00
fi
# create dns hideout directory
#
if [ ! -d " ${ adb_dnshidedir } " ]
then
mkdir -p -m 660 " ${ adb_dnshidedir } "
2017-01-24 02:03:41 +08:00
chown -R " ${ adb_dns } " :" ${ adb_dns } " " ${ adb_dnshidedir } " 2>/dev/null
2016-12-20 03:11:25 +08:00
else
rm -f " ${ adb_dnshidedir } / ${ adb_dnsprefix } " *
fi
# create adblock temp file/directory
#
adb_tmpload = " $( mktemp -tu) "
adb_tmpfile = " $( mktemp -tu) "
adb_tmpdir = " $( mktemp -p /tmp -d) "
# prepare whitelist entries
#
if [ -s " ${ adb_whitelist } " ]
then
awk " ${ adb_whitelist_rset } " " ${ adb_whitelist } " > " ${ adb_tmpdir } /tmp.whitelist "
fi
}
# f_rmtemp: remove temporary files & directories
#
f_rmtemp( )
{
rm -f " ${ adb_tmpload } "
rm -f " ${ adb_tmpfile } "
rm -rf " ${ adb_tmpdir } "
}
# f_rmdns: remove dns related files & directories
#
f_rmdns( )
{
rm -f " ${ adb_dnsdir } / ${ adb_dnsprefix } " *
rm -f " ${ adb_dir_backup } / ${ adb_dnsprefix } " *.gz
rm -rf " ${ adb_dnshidedir } "
2016-12-31 05:21:00 +08:00
ubus call service delete "{\"name\":\"adblock_stats\",\"instances\":\"stats\"}" 2>/dev/null
2016-12-20 03:11:25 +08:00
}
2017-01-24 02:03:41 +08:00
# f_dnsrestart: restart the dns backend
2016-12-20 03:11:25 +08:00
#
f_dnsrestart( )
{
local cnt = 0
dns_running = "false"
sync
2016-12-23 02:33:59 +08:00
killall -q -TERM " ${ adb_dns } "
2016-12-20 03:11:25 +08:00
while [ ${ cnt } -le 10 ]
do
2017-01-05 17:25:02 +08:00
dns_running = " $( ubus -S call service list " {\"name\":\" ${ adb_dns } \"} " | jsonfilter -l 1 -e " @. ${ adb_dns } .instances.*.running " ) "
2016-12-20 03:11:25 +08:00
if [ " ${ dns_running } " = "true" ]
then
2016-12-23 14:15:11 +08:00
return 0
2016-12-20 03:11:25 +08:00
fi
cnt = $(( cnt+1))
sleep 1
done
2016-12-23 14:15:11 +08:00
/etc/init.d/" ${ adb_dns } " restart
sleep 1
2016-12-20 03:11:25 +08:00
}
# f_list: backup/restore/remove block lists
#
f_list( )
{
local mode = " ${ 1 } "
if [ " ${ enabled_backup } " = "1" ] && [ -d " ${ adb_dir_backup } " ]
then
case " ${ mode } " in
backup)
gzip -cf " ${ adb_tmpfile } " > " ${ adb_dir_backup } / ${ adb_dnsprefix } . ${ src_name } .gz "
; ;
restore)
rm -f " ${ adb_dnsdir } / ${ adb_dnsprefix } . ${ src_name } "
if [ -f " ${ adb_dir_backup } / ${ adb_dnsprefix } . ${ src_name } .gz " ]
then
gunzip -cf " ${ adb_dir_backup } / ${ adb_dnsprefix } . ${ src_name } .gz " > " ${ adb_tmpfile } "
fi
; ;
remove)
rm -f " ${ adb_dnsdir } / ${ adb_dnsprefix } . ${ src_name } "
if [ -f " ${ adb_dir_backup } / ${ adb_dnsprefix } . ${ src_name } .gz " ]
then
rm -f " ${ adb_dir_backup } / ${ adb_dnsprefix } . ${ src_name } .gz "
fi
; ;
esac
fi
}
# f_switch: suspend/resume adblock processing
#
f_switch( )
{
if [ -d " ${ adb_dnshidedir } " ]
then
local source target status mode = " ${ 1 } "
local dns_active = " $( find " ${ adb_dnsdir } " -maxdepth 1 -type f -name " ${ adb_dnsprefix } * " -print) "
local dns_passive = " $( find " ${ adb_dnshidedir } " -maxdepth 1 -type f -name " ${ adb_dnsprefix } * " -print) "
if [ -n " ${ dns_active } " ] && [ " ${ mode } " = "suspend" ]
then
source = " ${ adb_dnsdir } / ${ adb_dnsprefix } "
target = " ${ adb_dnshidedir } "
status = "suspended"
elif [ -n " ${ dns_passive } " ] && [ " ${ mode } " = "resume" ]
then
source = " ${ adb_dnshidedir } / ${ adb_dnsprefix } "
target = " ${ adb_dnsdir } "
status = "resumed"
fi
if [ -n " ${ status } " ]
then
mv -f " ${ source } " * " ${ target } "
f_dnsrestart
f_log "info " " status ::: adblock processing ${ status } "
fi
fi
}
2016-12-23 14:15:11 +08:00
# f_query: query block lists for certain (sub-)domains
#
f_query( )
{
local search result cnt
local domain = " ${ 1 } "
local tld = " ${ domain #*. } "
local dns_active = " $( find " ${ adb_dnsdir } " -maxdepth 1 -type f -name " ${ adb_dnsprefix } * " -print) "
if [ -z " ${ dns_active } " ]
then
2017-01-06 23:10:18 +08:00
printf "%s\n" "::: no active block lists found, please start adblock first"
2016-12-23 14:15:11 +08:00
elif [ -z " ${ domain } " ] || [ " ${ domain } " = " ${ tld } " ]
then
2017-01-24 02:03:41 +08:00
printf "%s\n" "::: invalid domain input, please submit a specific (sub-)domain, e.g. 'www.abc.xyz'"
2016-12-23 14:15:11 +08:00
else
2017-01-06 23:10:18 +08:00
cd " ${ adb_dnsdir } "
2016-12-23 14:15:11 +08:00
while [ " ${ domain } " != " ${ tld } " ]
do
search = " ${ domain //./ \. } "
2017-01-06 23:10:18 +08:00
result = " $( grep -Hm1 " [/\"\.] ${ search } [/\"] " " ${ adb_dnsprefix } " * | awk -F ':|=|/|\"' '{printf(" %-20s : %s\n",$1,$4)}' ) "
printf "%s\n" " ::: distinct results for domain ' ${ domain } ' "
2016-12-23 14:15:11 +08:00
if [ -z " ${ result } " ]
then
2017-01-06 23:10:18 +08:00
printf "%s\n" " no match"
2016-12-23 14:15:11 +08:00
else
printf "%s\n" " ${ result } "
fi
domain = " ${ tld } "
tld = " ${ domain #*. } "
done
fi
}
2016-12-20 03:11:25 +08:00
# f_log: write to syslog, exit on error
#
f_log( )
{
local class = " ${ 1 } "
local log_msg = " ${ 2 } "
if [ -n " ${ log_msg } " ] && ( [ " ${ class } " != "debug" ] || [ ${ adb_debug } -eq 1 ] )
then
logger -t " adblock-[ ${ adb_ver } ] ${ class } " " ${ log_msg } "
if [ " ${ class } " = "error" ]
then
2017-01-24 02:03:41 +08:00
logger -t " adblock-[ ${ adb_ver } ] ${ class } " "Please also check the online documentation 'https://github.com/openwrt/packages/blob/master/net/adblock/files/README.md'"
2016-12-20 03:11:25 +08:00
f_rmtemp
f_rmdns
f_dnsrestart
exit 255
fi
fi
}
# f_debug: gather memory & space information
2017-01-20 16:57:33 +08:00
#
2016-12-20 03:11:25 +08:00
f_debug( )
{
local mem_total = 0 mem_free = 0 mem_swap = 0 tmp_space = 0 backup_space = 0
if [ ${ adb_debug } -eq 1 ]
then
mem_total = " $( awk '$1 ~ /^MemTotal/ {printf $2}' "/proc/meminfo" ) "
mem_free = " $( awk '$1 ~ /^MemFree/ {printf $2}' "/proc/meminfo" ) "
mem_swap = " $( awk '$1 ~ /^SwapTotal/ {printf $2}' "/proc/meminfo" ) "
f_log "debug" " memory ::: total: ${ mem_total } , free: ${ mem_free } , swap: ${ mem_swap } "
if [ -d " ${ adb_tmpdir } " ]
then
tmp_space = " $( df " ${ adb_tmpdir } " 2>/dev/null | tail -n1 | awk '{printf $4}' ) "
fi
if [ -d " ${ adb_dir_backup } " ]
then
backup_space = " $( df " ${ adb_dir_backup } " 2>/dev/null | tail -n1 | awk '{printf $4}' ) "
fi
f_log "debug" " space ::: tmp_dir: ${ adb_tmpdir } , tmp_kb: ${ tmp_space } , backup: ${ enabled_backup } , backup_dir: ${ adb_dir_backup } , backup_kb: ${ backup_space } "
fi
}
# main function for block list processing
#
f_main( )
{
2016-12-23 14:15:11 +08:00
local enabled url rc cnt sum_cnt = 0
2016-12-31 21:17:40 +08:00
local src_name src_rset shalla_file shalla_archive list active_lists
local sysver = " $( ubus -S call system board | jsonfilter -e '@.release.description' ) "
2016-12-20 03:11:25 +08:00
f_debug
2017-01-05 17:25:02 +08:00
f_log "debug" " main ::: dns-backend: ${ adb_dns } , fetch-tool: ${ adb_fetch } , parm: ${ adb_fetchparm } "
2016-12-20 03:11:25 +08:00
for src_name in ${ adb_sources }
do
eval " enabled=\"\${enabled_ ${ src_name } }\" "
eval " url=\"\${adb_src_ ${ src_name } }\" "
eval " src_rset=\"\${adb_src_rset_ ${ src_name } }\" "
adb_dnsfile = " ${ adb_dnsdir } / ${ adb_dnsprefix } . ${ src_name } "
> " ${ adb_tmpload } "
> " ${ adb_tmpfile } "
# basic pre-checks
#
if [ " ${ enabled } " = "0" ] || [ -z " ${ url } " ] || [ -z " ${ src_rset } " ]
then
f_list remove
continue
fi
# download block list
#
f_log "debug" " loop ::: name: ${ src_name } , enabled: ${ enabled } , dnsfile: ${ adb_dnsfile } "
if [ " ${ src_name } " = "blacklist" ]
then
cat " ${ url } " > " ${ adb_tmpload } "
rc = ${ ? }
elif [ " ${ src_name } " = "shalla" ]
then
shalla_archive = " ${ adb_tmpdir } /shallalist.tar.gz "
shalla_file = " ${ adb_tmpdir } /shallalist.txt "
" ${ adb_fetch } " ${ adb_fetchparm } " ${ shalla_archive } " " ${ url } "
rc = ${ ? }
if [ ${ rc } -eq 0 ]
then
> " ${ shalla_file } "
for category in ${ adb_src_cat_shalla }
do
tar -xOzf " ${ shalla_archive } " BL/${ category } /domains >> " ${ shalla_file } "
rc = ${ ? }
if [ ${ rc } -ne 0 ]
then
break
fi
done
cat " ${ shalla_file } " > " ${ adb_tmpload } "
rm -f " ${ shalla_file } "
fi
rm -f " ${ shalla_archive } "
rm -rf " ${ adb_tmpdir } /BL "
else
" ${ adb_fetch } " ${ adb_fetchparm } " ${ adb_tmpload } " " ${ url } "
rc = ${ ? }
fi
# check download result and prepare domain output (incl. list backup/restore)
#
f_log "debug" " loop ::: name: ${ src_name } , load-rc: ${ rc } "
if [ ${ rc } -eq 0 ] && [ -s " ${ adb_tmpload } " ]
then
awk " ${ src_rset } " " ${ adb_tmpload } " > " ${ adb_tmpfile } "
if [ -s " ${ adb_tmpfile } " ]
then
f_list backup
else
f_list restore
fi
else
f_list restore
fi
# remove whitelist domains, sort and make them unique, final list preparation
#
if [ -s " ${ adb_tmpfile } " ]
then
if [ -s " ${ adb_tmpdir } /tmp.whitelist " ]
then
grep -vf " ${ adb_tmpdir } /tmp.whitelist " " ${ adb_tmpfile } " | sort -u | eval " ${ adb_dnsformat } " > " ${ adb_dnsfile } "
else
sort -u " ${ adb_tmpfile } " | eval " ${ adb_dnsformat } " > " ${ adb_dnsfile } "
fi
rc = ${ ? }
if [ ${ rc } -ne 0 ]
then
f_list remove
fi
fi
f_log "debug" " loop ::: name: ${ src_name } , list-rc: ${ rc } "
done
2017-01-05 17:25:02 +08:00
# sort block lists
2016-12-20 03:11:25 +08:00
#
2016-12-23 02:33:59 +08:00
for src_name in $( ls -dASr " ${ adb_dnsdir } / ${ adb_dnsprefix } " * 2>/dev/null)
2016-12-20 03:11:25 +08:00
do
if [ -s " ${ adb_tmpdir } /blocklist.overall " ]
then
2016-12-23 02:33:59 +08:00
sort " ${ adb_tmpdir } /blocklist.overall " " ${ adb_tmpdir } /blocklist.overall " " ${ src_name } " | uniq -u > " ${ adb_tmpdir } /tmp.blocklist "
cat " ${ adb_tmpdir } /tmp.blocklist " > " ${ src_name } "
2016-12-20 03:11:25 +08:00
fi
2016-12-23 02:33:59 +08:00
cat " ${ src_name } " >> " ${ adb_tmpdir } /blocklist.overall "
cnt = " $( wc -l < " ${ src_name } " ) "
sum_cnt = $(( sum_cnt + cnt))
2016-12-31 05:21:00 +08:00
list = " ${ src_name /*./ } "
2016-12-31 21:17:40 +08:00
if [ -z " ${ active_lists } " ]
2016-12-31 05:21:00 +08:00
then
2016-12-31 21:17:40 +08:00
active_lists = " \" ${ list } \":\" ${ cnt } \" "
2016-12-31 05:21:00 +08:00
else
2016-12-31 21:17:40 +08:00
active_lists = " ${ active_lists } ,\" ${ list } \":\" ${ cnt } \" "
2016-12-31 05:21:00 +08:00
fi
2016-12-20 03:11:25 +08:00
done
2017-01-05 17:25:02 +08:00
2017-01-24 02:03:41 +08:00
# restart the dns backend and write statistics to procd service instance
2017-01-05 17:25:02 +08:00
#
chown " ${ adb_dns } " :" ${ adb_dns } " " ${ adb_dnsdir } / ${ adb_dnsprefix } " * 2>/dev/null
2016-12-20 03:11:25 +08:00
f_dnsrestart
if [ " ${ dns_running } " = "true" ]
then
f_debug
f_rmtemp
2016-12-31 21:17:40 +08:00
f_log "info " " status ::: block lists with overall ${ sum_cnt } domains loaded ( ${ sysver } ) "
2016-12-31 05:21:00 +08:00
ubus call service add " {\"name\":\"adblock_stats\",
2016-12-31 21:17:40 +08:00
\" instances\" :{ \" stats\" :{ \" command\" :[ \" \" ] ,
2017-01-05 17:25:02 +08:00
\" data\" :{ \" active_lists\" :[ { ${ active_lists } } ] ,
\" adblock_version\" :\" ${ adb_ver } \" ,
\" blocked_domains\" :\" ${ sum_cnt } \" ,
\" dns_backend\" :\" ${ adb_dns } \" ,
2016-12-31 21:17:40 +08:00
\" last_rundate\" :\" $( /bin/date "+%d.%m.%Y %H:%M:%S" ) \" ,
\" system\" :\" ${ sysver } \" } } } } "
return 0
2016-12-20 03:11:25 +08:00
fi
2016-12-31 21:17:40 +08:00
f_debug
2017-01-24 02:03:41 +08:00
f_log "error" " status ::: dns backend restart with active block lists failed ( ${ sysver } ) "
2016-12-20 03:11:25 +08:00
}
# handle different adblock actions
#
if [ " ${ adb_procd } " = "true" ]
then
f_envload
case " ${ 1 } " in
stop)
f_rmtemp
f_rmdns
f_dnsrestart
; ;
suspend )
f_switch suspend
; ;
resume)
f_switch resume
; ;
2016-12-23 14:15:11 +08:00
query)
f_query " ${ 2 } "
; ;
2016-12-20 03:11:25 +08:00
*)
f_envcheck
f_main
; ;
esac
fi
2017-01-24 02:03:41 +08:00
exit 0