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.

378 lines
13 KiB

  1. #!/bin/sh
  2. #######################################################
  3. # ad/abuse domain blocking script for dnsmasq/openwrt #
  4. # written by Dirk Brenken (dirk@brenken.org) #
  5. #######################################################
  6. # LICENSE
  7. # ========
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. ###############
  21. # environment #
  22. ###############
  23. # set script version
  24. #
  25. adb_version="0.40.2"
  26. # get current pid, script directory and openwrt version
  27. #
  28. pid=${$}
  29. adb_scriptdir="${0%/*}"
  30. openwrt_version="$(cat /etc/openwrt_version 2>/dev/null)"
  31. # source in adblock function library
  32. #
  33. if [ -r "${adb_scriptdir}/adblock-helper.sh" ]
  34. then
  35. . "${adb_scriptdir}/adblock-helper.sh" 2>/dev/null
  36. else
  37. rc=600
  38. /usr/bin/logger -s -t "adblock[${pid}] error" "adblock function library not found, rc: ${rc}"
  39. exit ${rc}
  40. fi
  41. ################
  42. # main program #
  43. ################
  44. # call restore function on trap signals (HUP, INT, QUIT, BUS, SEGV, TERM)
  45. #
  46. trap "f_log 'trap error' '700'; f_restore" 1 2 3 10 11 15
  47. # start logging
  48. #
  49. f_log "domain adblock processing started (${adb_version}, ${openwrt_version}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  50. # load environment
  51. #
  52. f_envload
  53. # parse environment
  54. #
  55. f_envparse
  56. # check environment
  57. #
  58. f_envcheck
  59. # start shallalist (pre-)processing
  60. #
  61. if [ -n "${adb_arc_shalla}" ]
  62. then
  63. # start shallalist processing
  64. #
  65. shalla_archive="${adb_tmpdir}/shallalist.tar.gz"
  66. shalla_file="${adb_tmpdir}/shallalist.txt"
  67. src_name="shalla"
  68. adb_dnsfile="${adb_dnsdir}/${adb_dnsprefix}.${src_name}"
  69. list_time="$(grep -F "# last modified: " "${adb_dnsfile}" 2>/dev/null)"
  70. list_time="${list_time/*: /}"
  71. # only process shallalist archive with updated timestamp
  72. #
  73. shalla_time="$(curl ${curl_parm} --max-time 5 --head "${adb_arc_shalla}" 2>/dev/null | grep -F "Last-Modified: " 2>/dev/null | tr -d '\r' 2>/dev/null)"
  74. shalla_time="${shalla_time/*: /}"
  75. if [ -z "${shalla_time}" ]
  76. then
  77. shalla_time="$(date)"
  78. f_log "no online timestamp received, current date will be used (${src_name})"
  79. fi
  80. if [ -z "${list_time}" ] || [ "${list_time}" != "${shalla_time}" ]
  81. then
  82. f_log "shallalist (pre-)processing started ..."
  83. curl ${curl_parm} --max-time "${adb_maxtime}" "${adb_arc_shalla}" --output "${shalla_archive}" 2>/dev/null
  84. rc=${?}
  85. if [ $((rc)) -ne 0 ]
  86. then
  87. f_log "source download failed (${src_name})" "${rc}"
  88. f_restore
  89. fi
  90. # extract and merge only domains of selected shallalist categories
  91. #
  92. > "${shalla_file}"
  93. for category in ${adb_cat_shalla}
  94. do
  95. tar -xOzf "${shalla_archive}" BL/${category}/domains 2>/dev/null >> "${shalla_file}"
  96. rc=${?}
  97. if [ $((rc)) -ne 0 ]
  98. then
  99. f_log "source archive extraction failed (${category})" "${rc}"
  100. f_restore
  101. fi
  102. done
  103. # remove temporary files
  104. #
  105. rm -f "${shalla_archive}" >/dev/null 2>&1
  106. rm -rf "${adb_tmpdir}/BL" >/dev/null 2>&1
  107. fi
  108. adb_sources="${adb_sources} file:///${shalla_file}&ruleset=rset_shalla"
  109. fi
  110. # add blacklist source to active adblock domain sources
  111. #
  112. if [ -n "${adb_sources}" ] && [ -s "${adb_blacklist}" ]
  113. then
  114. adb_sources="${adb_sources} file://${adb_blacklist}&ruleset=rset_blacklist"
  115. fi
  116. # loop through active adblock domain sources,
  117. # prepare output and store all extracted domains in temp file
  118. #
  119. for src in ${adb_sources}
  120. do
  121. # download selected adblock sources
  122. #
  123. url="${src/\&ruleset=*/}"
  124. check_url="$(printf "${url}" | sed -n '/^https:/p' 2>/dev/null)"
  125. src_name="${src/*\&ruleset=rset_/}"
  126. adb_dnsfile="${adb_dnsdir}/${adb_dnsprefix}.${src_name}"
  127. list_time="$(grep -F "# last modified: " "${adb_dnsfile}" 2>/dev/null)"
  128. list_time="${list_time/*: /}"
  129. # prepare find statement for all active adblocklist sources
  130. #
  131. if [ -z "${adb_srcfind}" ]
  132. then
  133. adb_srcfind="! -name ${adb_dnsprefix}.${src_name}"
  134. else
  135. adb_srcfind="${adb_srcfind} -a ! -name ${adb_dnsprefix}.${src_name}"
  136. fi
  137. # wget/curl switch
  138. # only download blocklist with newer/updated timestamp
  139. #
  140. if [ -n "${check_url}" ]
  141. then
  142. url_time="$(wget ${wget_parm} --timeout=5 --server-response --spider "${url}" 2>&1 | grep -F "Last-Modified: " 2>/dev/null | tr -d '\r' 2>/dev/null)"
  143. url_time="${url_time/*: /}"
  144. if [ -z "${url_time}" ]
  145. then
  146. url_time="$(date)"
  147. f_log "no online timestamp received, current date will be used (${src_name})"
  148. fi
  149. if [ -z "${list_time}" ] || [ "${list_time}" != "${url_time}" ]
  150. then
  151. tmp_domains="$(wget ${wget_parm} --timeout="${adb_maxtime}" --tries=1 --output-document=- "${url}" 2>/dev/null)"
  152. rc=${?}
  153. else
  154. f_log "source doesn't change, no update required (${src_name})"
  155. continue
  156. fi
  157. else
  158. if [ "${src_name}" = "shalla" ]
  159. then
  160. url_time="${shalla_time}"
  161. else
  162. url_time="$(curl ${curl_parm} --max-time 5 --head "${url}" 2>/dev/null | grep -F "Last-Modified: " | tr -d '\r')"
  163. url_time="${url_time/*: /}"
  164. fi
  165. if [ -z "${url_time}" ]
  166. then
  167. url_time="$(date)"
  168. f_log "no online timestamp received, current date will be used (${src_name})"
  169. fi
  170. if [ -z "${list_time}" ] || [ "${list_time}" != "${url_time}" ]
  171. then
  172. tmp_domains="$(curl ${curl_parm} --max-time "${adb_maxtime}" "${url}" 2>/dev/null)"
  173. rc=${?}
  174. else
  175. f_log "source doesn't change, no update required (${src_name})"
  176. continue
  177. fi
  178. fi
  179. # check download result and prepare domain output by regex patterns
  180. #
  181. if [ $((rc)) -eq 0 ] && [ -n "${tmp_domains}" ]
  182. then
  183. eval "$(printf "${src}" | sed 's/\(.*\&ruleset=\)/ruleset=\$/g')"
  184. count="$(printf "%s\n" "${tmp_domains}" | tr '[A-Z]' '[a-z]' | eval "${ruleset}" | tee "${adb_tmpfile}" | wc -l)"
  185. f_log "source download finished (${url}, ${count} entries)"
  186. if [ "${src_name}" = "shalla" ]
  187. then
  188. rm -f "${shalla_file}" >/dev/null 2>&1
  189. fi
  190. unset tmp_domains
  191. elif [ $((rc)) -eq 0 ] && [ -z "${tmp_domains}" ]
  192. then
  193. f_log "empty source download finished (${src_name})"
  194. continue
  195. else
  196. f_log "source download failed (${src_name})" "${rc}"
  197. f_restore
  198. fi
  199. # remove whitelist domains, sort domains and make them unique,
  200. # finally rewrite ad/abuse domain information to separate dnsmasq files
  201. #
  202. if [ $((count)) -gt 0 ] && [ -n "${adb_tmpfile}" ]
  203. then
  204. if [ -s "${adb_whitelist}" ]
  205. then
  206. grep -Fvxf "${adb_whitelist}" "${adb_tmpfile}" 2>/dev/null | sort 2>/dev/null | uniq -u 2>/dev/null | eval "${adb_dnsformat}" 2>/dev/null > "${adb_dnsfile}"
  207. rc=${?}
  208. else
  209. sort "${adb_tmpfile}" 2>/dev/null | uniq -u 2>/dev/null | eval "${adb_dnsformat}" 2>/dev/null > "${adb_dnsfile}"
  210. rc=${?}
  211. fi
  212. # prepare find statement for revised adblocklist sources
  213. #
  214. if [ -z "${adb_revsrcfind}" ]
  215. then
  216. adb_revsrcfind="-name ${adb_dnsprefix}.${src_name}"
  217. else
  218. adb_revsrcfind="${adb_revsrcfind} -o -name ${adb_dnsprefix}.${src_name}"
  219. fi
  220. # write preliminary adblocklist footer
  221. #
  222. if [ $((rc)) -eq 0 ]
  223. then
  224. count="$(wc -l < "${adb_dnsdir}/${adb_dnsprefix}.${src_name}")"
  225. printf "%s\n" "#------------------------------------------------------------------" >> "${adb_dnsfile}"
  226. printf "%s\n" "# ${0##*/} (${adb_version}) - ${count} ad/abuse domains blocked" >> "${adb_dnsfile}"
  227. printf "%s\n" "# source: ${url}" >> "${adb_dnsfile}"
  228. printf "%s\n" "# last modified: ${url_time}" >> "${adb_dnsfile}"
  229. f_log "domain merging finished (${src_name})"
  230. else
  231. f_log "domain merging failed (${src_name})" "${rc}"
  232. f_restore
  233. fi
  234. else
  235. f_log "empty domain input received (${src_name})"
  236. continue
  237. fi
  238. done
  239. # remove old adblocklists and their backups
  240. #
  241. if [ -n "${adb_srcfind}" ]
  242. then
  243. find "${adb_dnsdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}.*" \( ${adb_srcfind} \) -exec rm -f "{}" \; 2>/dev/null
  244. if [ $((rc)) -ne 0 ]
  245. then
  246. f_log "error during removal of old adblocklists" "${rc}"
  247. f_remove
  248. fi
  249. if [ "${backup_ok}" = "true" ]
  250. then
  251. find "${adb_backupdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}.*" \( ${adb_srcfind} \) -exec rm -f "{}" \; 2>/dev/null
  252. if [ $((rc)) -ne 0 ]
  253. then
  254. f_log "error during removal of old backups" "${rc}"
  255. f_remove
  256. fi
  257. fi
  258. else
  259. rm -f "${adb_dnsdir}/${adb_dnsprefix}."* >/dev/null 2>&1
  260. if [ "${backup_ok}" = "true" ]
  261. then
  262. rm -f "${adb_backupdir}/${adb_dnsprefix}."* >/dev/null 2>&1
  263. f_log "all available adblocklists and backups removed"
  264. else
  265. f_log "all available adblocklists removed"
  266. fi
  267. fi
  268. # make separate adblocklists unique
  269. #
  270. if [ $((adb_unique)) -eq 1 ]
  271. then
  272. if [ -n "${adb_revsrcfind}" ]
  273. then
  274. f_log "remove duplicates in separate adblocklists ..."
  275. # generate a temporary, unique overall list
  276. #
  277. head -qn -4 "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null | sort -u 2>/dev/null > "${adb_dnsdir}/tmp.overall"
  278. # loop through all separate lists, ordered by size (ascending)
  279. #
  280. for list in $(ls -Sr "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null)
  281. do
  282. # check separate lists vs. overall list,
  283. # rewrite only duplicate entries back to separate lists
  284. #
  285. list="${list/*./}"
  286. sort "${adb_dnsdir}/tmp.overall" "${adb_dnsdir}/${adb_dnsprefix}.${list}" 2>/dev/null | uniq -d 2>/dev/null > "${adb_dnsdir}/tmp.${list}"
  287. # remove these entries from overall list,
  288. # rewrite only unique entries back to overall list
  289. #
  290. tmp_unique="$(sort "${adb_dnsdir}/tmp.overall" "${adb_dnsdir}/tmp.${list}" 2>/dev/null | uniq -u 2>/dev/null)"
  291. printf "%s\n" "${tmp_unique}" > "${adb_dnsdir}/tmp.overall"
  292. # write final adblocklist footer
  293. #
  294. count="$(wc -l < "${adb_dnsdir}/tmp.${list}")"
  295. printf "%s\n" "#------------------------------------------------------------------" >> "${adb_dnsdir}/tmp.${list}"
  296. printf "%s\n" "# ${0##*/} (${adb_version}) - ${count} ad/abuse domains blocked" >> "${adb_dnsdir}/tmp.${list}"
  297. tail -qn -2 "${adb_dnsdir}/$adb_dnsprefix.${list}" 2>/dev/null >> "${adb_dnsdir}/tmp.${list}"
  298. mv -f "${adb_dnsdir}/tmp.${list}" "${adb_dnsdir}/${adb_dnsprefix}.${list}" >/dev/null 2>&1
  299. done
  300. rm -f "${adb_dnsdir}/tmp.overall" >/dev/null 2>&1
  301. fi
  302. fi
  303. # restart dnsmasq with newly generated block lists
  304. #
  305. /etc/init.d/dnsmasq restart >/dev/null 2>&1
  306. sleep 3
  307. # dnsmasq health check
  308. #
  309. dns_status="$(logread -l 20 -e "dnsmasq" -e "FAILED to start up" 2>/dev/null)"
  310. if [ -z "${dns_status}" ]
  311. then
  312. dns_status="$(nslookup "${adb_domain}" 2>/dev/null | grep -F "${adb_ip}" 2>/dev/null)"
  313. if [ -z "${dns_status}" ]
  314. then
  315. adb_count="$(head -qn -4 "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null | wc -l)"
  316. if [ "${backup_ok}" = "true" ]
  317. then
  318. if [ -n "${adb_revsrcfind}" ]
  319. then
  320. find "${adb_dnsdir}" -maxdepth 1 -type f \( ${adb_revsrcfind} \) -exec cp -f "{}" "${adb_backupdir}" \; 2>/dev/null
  321. rc=${?}
  322. if [ $((rc)) -ne 0 ]
  323. then
  324. f_log "error during backup of adblocklists" "${rc}"
  325. f_remove
  326. fi
  327. f_log "adblocklists with overall ${adb_count} domains loaded, new backups generated"
  328. else
  329. f_log "adblocklists with overall ${adb_count} domains loaded, no new backups"
  330. fi
  331. else
  332. f_log "adblocklists with overall ${adb_count} domains loaded, backups disabled"
  333. fi
  334. else
  335. rc=605
  336. f_log "nslookup probe failed" "${rc}"
  337. f_restore
  338. fi
  339. else
  340. rc=610
  341. f_log "dnsmasq probe failed" "${rc}"
  342. f_restore
  343. fi
  344. # remove temporary files and exit
  345. #
  346. f_remove