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.

334 lines
10 KiB

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