You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
11 KiB

  1. #!/bin/sh
  2. # dns based ad/abuse domain blocking script
  3. # written by Dirk Brenken (dev@brenken.org)
  4. # This is free software, licensed under the GNU General Public License v3.
  5. # You should have received a copy of the GNU General Public License
  6. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  7. # set the C locale
  8. #
  9. LC_ALL=C
  10. # script debug switch (disabled by default)
  11. # set 'DEBUG=1' to enable script debugging
  12. #
  13. DEBUG=0
  14. if [ $((DEBUG)) -eq 0 ]
  15. then
  16. exec 2>/dev/null
  17. fi
  18. # set pid & logger
  19. #
  20. adb_pid="${$}"
  21. adb_pidfile="/var/run/adblock.pid"
  22. adb_log="$(which logger)"
  23. if [ -r "${adb_pidfile}" ]
  24. then
  25. rc=255
  26. "${adb_log}" -s -t "adblock[${adb_pid}] error" "adblock service already running ($(cat ${adb_pidfile}))"
  27. exit ${rc}
  28. else
  29. printf "${adb_pid}" > "${adb_pidfile}"
  30. fi
  31. # get current directory and set script/config version
  32. #
  33. adb_scriptdir="${0%/*}"
  34. adb_scriptver="1.1.11"
  35. adb_mincfgver="1.2"
  36. # source in adblock function library
  37. #
  38. if [ -r "${adb_scriptdir}/adblock-helper.sh" ]
  39. then
  40. . "${adb_scriptdir}/adblock-helper.sh"
  41. else
  42. rc=254
  43. "${adb_log}" -s -t "adblock[${adb_pid}] error" "adblock function library not found"
  44. rm -f "${adb_pidfile}"
  45. exit ${rc}
  46. fi
  47. # call trap function on error signals (HUP, INT, QUIT, BUS, SEGV, TERM)
  48. #
  49. trap "rc=250; f_log 'error signal received/trapped' '${rc}'; f_exit" 1 2 3 10 11 15
  50. # load environment
  51. #
  52. f_envload
  53. # start logging
  54. #
  55. f_log "domain adblock processing started (${adb_scriptver}, ${adb_sysver}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  56. # check environment
  57. #
  58. f_envcheck
  59. # loop through active adblock domain sources,
  60. # download sources, prepare output and store all extracted domains in temp file
  61. #
  62. for src_name in ${adb_sources}
  63. do
  64. eval "url=\"\${adb_src_${src_name}}\""
  65. eval "src_rset=\"\${adb_src_rset_${src_name}}\""
  66. adb_dnsfile="${adb_dnsdir}/${adb_dnsprefix}.${src_name}"
  67. list_time="$(${adb_uci} -q get "adblock.${src_name}.adb_src_timestamp")"
  68. f_log "=> processing adblock source '${src_name}'"
  69. # check 'url' and 'src_rset' values
  70. #
  71. if [ -z "${url}" ] || [ -z "${src_rset}" ]
  72. then
  73. "${adb_uci}" -q set "adblock.${src_name}.adb_src_timestamp=broken config"
  74. f_log " broken source configuration, check 'adb_src' and 'adb_src_rset' in config"
  75. continue
  76. fi
  77. # prepare find statement with active adblock list sources
  78. #
  79. if [ -z "${adb_srclist}" ]
  80. then
  81. adb_srclist="! -name ${adb_dnsprefix}.${src_name}*"
  82. else
  83. adb_srclist="${adb_srclist} -a ! -name ${adb_dnsprefix}.${src_name}*"
  84. fi
  85. # only download adblock list with newer/updated timestamp
  86. #
  87. if [ "${src_name}" = "blacklist" ]
  88. then
  89. url_time="$(date -r "${url}")"
  90. else
  91. url_time="$(${adb_fetch} ${fetch_parm} --server-response --spider "${url}" 2>&1 | awk '$0 ~ /Last-Modified/ {printf substr($0,18)}')"
  92. fi
  93. if [ -z "${url_time}" ]
  94. then
  95. url_time="$(date)"
  96. f_log " no online timestamp received, current date will be used"
  97. fi
  98. if [ -z "${list_time}" ] || [ "${list_time}" != "${url_time}" ] || [ ! -r "${adb_dnsfile}" ] ||\
  99. ([ "${backup_ok}" = "true" ] && [ ! -r "${adb_dir_backup}/${adb_dnsprefix}.${src_name}.gz" ])
  100. then
  101. if [ "${src_name}" = "blacklist" ]
  102. then
  103. tmp_domains="$(cat "${url}")"
  104. rc=${?}
  105. elif [ "${src_name}" = "shalla" ]
  106. then
  107. shalla_archive="${adb_tmpdir}/shallalist.tar.gz"
  108. shalla_file="${adb_tmpdir}/shallalist.txt"
  109. "${adb_fetch}" ${fetch_parm} --output-document="${shalla_archive}" "${url}"
  110. rc=${?}
  111. if [ $((rc)) -eq 0 ]
  112. then
  113. > "${shalla_file}"
  114. for category in ${adb_src_cat_shalla}
  115. do
  116. tar -xOzf "${shalla_archive}" BL/${category}/domains >> "${shalla_file}"
  117. rc=${?}
  118. if [ $((rc)) -ne 0 ]
  119. then
  120. f_log " archive extraction failed (${category})"
  121. break
  122. fi
  123. done
  124. rm -f "${shalla_archive}"
  125. rm -rf "${adb_tmpdir}/BL"
  126. tmp_domains="$(cat "${shalla_file}")"
  127. rc=${?}
  128. fi
  129. else
  130. tmp_domains="$(${adb_fetch} ${fetch_parm} --output-document=- "${url}")"
  131. rc=${?}
  132. fi
  133. else
  134. f_log " source doesn't change, no update required"
  135. continue
  136. fi
  137. # check download result and prepare domain output by regex patterns
  138. #
  139. if [ $((rc)) -eq 0 ] && [ -n "${tmp_domains}" ]
  140. then
  141. count="$(printf "%s\n" "${tmp_domains}" | awk "${src_rset}" | tee "${adb_tmpfile}" | wc -l)"
  142. f_log " source download finished (${count} entries)"
  143. if [ "${src_name}" = "shalla" ]
  144. then
  145. rm -f "${shalla_file}"
  146. fi
  147. unset tmp_domains
  148. elif [ $((rc)) -eq 0 ] && [ -z "${tmp_domains}" ]
  149. then
  150. "${adb_uci}" -q set "adblock.${src_name}.adb_src_timestamp=empty download"
  151. f_log " empty source download finished"
  152. continue
  153. else
  154. rc=0
  155. if [ -z "${adb_errsrclist}" ]
  156. then
  157. adb_errsrclist="-name ${adb_dnsprefix}.${src_name}.gz"
  158. else
  159. adb_errsrclist="${adb_errsrclist} -o -name ${adb_dnsprefix}.${src_name}.gz"
  160. fi
  161. "${adb_uci}" -q set "adblock.${src_name}.adb_src_timestamp=download failed"
  162. f_log " source download failed"
  163. continue
  164. fi
  165. # remove whitelist domains, sort domains and make them unique,
  166. # finally rewrite ad/abuse domain information to separate dnsmasq files
  167. #
  168. if [ $((count)) -gt 0 ] && [ -n "${adb_tmpfile}" ]
  169. then
  170. if [ -s "${adb_tmpdir}/tmp.whitelist" ]
  171. then
  172. grep -vf "${adb_tmpdir}/tmp.whitelist" "${adb_tmpfile}" | sort -u | eval "${adb_dnsformat}" > "${adb_dnsfile}"
  173. rc=${?}
  174. else
  175. sort -u "${adb_tmpfile}" | eval "${adb_dnsformat}" > "${adb_dnsfile}"
  176. rc=${?}
  177. fi
  178. # prepare find statement with revised adblock list sources
  179. #
  180. if [ -z "${adb_revsrclist}" ]
  181. then
  182. adb_revsrclist="-name ${adb_dnsprefix}.${src_name}"
  183. else
  184. adb_revsrclist="${adb_revsrclist} -o -name ${adb_dnsprefix}.${src_name}"
  185. fi
  186. # store source timestamp in config
  187. #
  188. if [ $((rc)) -eq 0 ]
  189. then
  190. "${adb_uci}" -q set "adblock.${src_name}.adb_src_timestamp=${url_time}"
  191. f_log " domain merging finished"
  192. else
  193. f_log " domain merging failed" "${rc}"
  194. f_restore
  195. fi
  196. else
  197. "${adb_uci}" -q set "adblock.${src_name}.adb_src_timestamp=empty domain input"
  198. f_log " empty domain input received"
  199. continue
  200. fi
  201. done
  202. # remove disabled adblock lists and their backups
  203. #
  204. if [ -n "${adb_srclist}" ]
  205. then
  206. rm_done="$(find "${adb_dnsdir}" -maxdepth 1 -type f \( ${adb_srclist} \) -print -exec rm -f "{}" \;)"
  207. rc=${?}
  208. if [ "${backup_ok}" = "true" ] && [ -n "${rm_done}" ]
  209. then
  210. find "${adb_dir_backup}" -maxdepth 1 -type f \( ${adb_srclist} \) -exec rm -f "{}" \;
  211. fi
  212. else
  213. rm_done="$(find "${adb_dnsdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}*" -print -exec rm -f "{}" \;)"
  214. rc=${?}
  215. if [ "${backup_ok}" = "true" ]
  216. then
  217. find "${adb_dir_backup}" -maxdepth 1 -type f -name "${adb_dnsprefix}*" -exec rm -f "{}" \;
  218. fi
  219. fi
  220. if [ $((rc)) -eq 0 ] && [ -n "${rm_done}" ]
  221. then
  222. f_rmconfig "${rm_done}"
  223. f_log "remove disabled adblock lists"
  224. elif [ $((rc)) -ne 0 ] && [ -n "${rm_done}" ]
  225. then
  226. f_log "error during removal of disabled adblock lists" "${rc}"
  227. f_exit
  228. fi
  229. # partial restore of adblock lists in case of download errors
  230. #
  231. if [ "${backup_ok}" = "true" ] && [ -n "${adb_errsrclist}" ]
  232. then
  233. restore_done="$(find "${adb_dir_backup}" -maxdepth 1 -type f \( ${adb_errsrclist} \) -print -exec cp -pf "{}" "${adb_dnsdir}" \;)"
  234. rc=${?}
  235. if [ $((rc)) -eq 0 ] && [ -n "${restore_done}" ]
  236. then
  237. find "${adb_dnsdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}*.gz" -exec gunzip -f "{}" \;
  238. f_rmconfig "${restore_done}" "true"
  239. f_log "partial restore done"
  240. elif [ $((rc)) -ne 0 ]
  241. then
  242. f_log "error during partial restore" "${rc}"
  243. f_exit
  244. fi
  245. fi
  246. # make separate adblock lists entries unique
  247. #
  248. if [ "${mem_ok}" = "true" ] && [ -n "${adb_revsrclist}" ]
  249. then
  250. f_log "remove duplicates in separate adblock lists"
  251. # generate a unique overall block list
  252. #
  253. sort -u "${adb_dnsdir}/${adb_dnsprefix}."* > "${adb_tmpdir}/blocklist.overall"
  254. # loop through all separate lists, ordered by size (ascending)
  255. #
  256. for list in $(ls -ASr "${adb_dnsdir}/${adb_dnsprefix}"*)
  257. do
  258. # check overall block list vs. separate block list,
  259. # write all duplicate entries to separate list
  260. #
  261. list="${list/*./}"
  262. sort "${adb_tmpdir}/blocklist.overall" "${adb_dnsdir}/${adb_dnsprefix}.${list}" | uniq -d > "${adb_tmpdir}/tmp.${list}"
  263. mv -f "${adb_tmpdir}/tmp.${list}" "${adb_dnsdir}/${adb_dnsprefix}.${list}"
  264. # write all unique entries back to overall block list
  265. #
  266. sort "${adb_tmpdir}/blocklist.overall" "${adb_dnsdir}/${adb_dnsprefix}.${list}" | uniq -u > "${adb_tmpdir}/tmp.overall"
  267. mv -f "${adb_tmpdir}/tmp.overall" "${adb_tmpdir}/blocklist.overall"
  268. done
  269. rm -f "${adb_tmpdir}/blocklist.overall"
  270. fi
  271. # restart & check dnsmasq with newly generated set of adblock lists
  272. #
  273. f_cntconfig
  274. adb_count="$(${adb_uci} -q get "adblock.global.adb_overall_count")"
  275. if [ -n "${adb_revsrclist}" ] || [ -n "${rm_done}" ] || [ -n "${restore_done}" ]
  276. then
  277. /etc/init.d/dnsmasq restart
  278. sleep 1
  279. rc="$(ps | grep -q "[d]nsmasq"; printf ${?})"
  280. if [ $((rc)) -eq 0 ]
  281. then
  282. f_log "adblock lists with overall ${adb_count} domains loaded"
  283. else
  284. rc=100
  285. f_log "dnsmasq restart failed, please check 'logread' output" "${rc}"
  286. f_restore
  287. fi
  288. else
  289. f_log "adblock lists with overall ${adb_count} domains are still valid, no update required"
  290. fi
  291. # create adblock list backups
  292. #
  293. if [ "${backup_ok}" = "true" ] && [ -n "${adb_revsrclist}" ]
  294. then
  295. backup_done="$(find "${adb_dnsdir}" -maxdepth 1 -type f \( ${adb_revsrclist} \) -print -exec cp -pf "{}" "${adb_dir_backup}" \;)"
  296. rc=${?}
  297. if [ $((rc)) -eq 0 ] && [ -n "${backup_done}" ]
  298. then
  299. find "${adb_dir_backup}" -maxdepth 1 -type f \( -name "${adb_dnsprefix}*" -a ! -name "${adb_dnsprefix}*.gz" \) -exec gzip -f "{}" \;
  300. f_log "new adblock list backups generated"
  301. elif [ $((rc)) -ne 0 ] && [ -n "${backup_done}" ]
  302. then
  303. f_log "error during backup of adblock lists" "${rc}"
  304. f_exit
  305. fi
  306. fi
  307. # remove temporary files and exit
  308. #
  309. f_exit