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.

416 lines
14 KiB

  1. #!/bin/sh
  2. #######################################################
  3. # ad/abuse domain blocking script for dnsmasq/openwrt #
  4. # written by Dirk Brenken (openwrt@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 the C locale, characters are single bytes, the charset is ASCII
  24. # speeds up things like sort, grep etc.
  25. #
  26. LC_ALL=C
  27. # set script version
  28. #
  29. adb_version="0.60.1"
  30. # get current pid, script directory and openwrt version
  31. #
  32. pid=${$}
  33. adb_scriptdir="${0%/*}"
  34. openwrt_version="$(cat /etc/openwrt_version 2>/dev/null)"
  35. # source in adblock function library
  36. #
  37. if [ -r "${adb_scriptdir}/adblock-helper.sh" ]
  38. then
  39. . "${adb_scriptdir}/adblock-helper.sh" 2>/dev/null
  40. else
  41. rc=100
  42. /usr/bin/logger -s -t "adblock[${pid}] error" "adblock function library not found, rc: ${rc}"
  43. exit ${rc}
  44. fi
  45. ################
  46. # main program #
  47. ################
  48. # call exit function on trap signals (HUP, INT, QUIT, BUS, SEGV, TERM)
  49. #
  50. trap "rc=255; f_log 'error signal received/trapped' '${rc}'; f_exit" 1 2 3 10 11 15
  51. # start logging
  52. #
  53. f_log "domain adblock processing started (${adb_version}, ${openwrt_version}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  54. # load environment
  55. #
  56. f_envload
  57. # parse environment
  58. #
  59. f_envparse
  60. # check environment
  61. #
  62. f_envcheck
  63. # start shallalist (pre-)processing
  64. #
  65. if [ -n "${adb_arc_shalla}" ]
  66. then
  67. # start shallalist processing
  68. #
  69. shalla_archive="${adb_tmpdir}/shallalist.tar.gz"
  70. shalla_file="${adb_tmpdir}/shallalist.txt"
  71. src_name="shalla"
  72. adb_dnsfile="${adb_dnsdir}/${adb_dnsprefix}.${src_name}"
  73. list_time="$(grep -F "# last modified: " "${adb_dnsfile}" 2>/dev/null)"
  74. list_time="${list_time/*: /}"
  75. f_log "=> (pre-)processing adblock source '${src_name}'"
  76. # only process shallalist archive with updated timestamp,
  77. # extract and merge only domains of selected shallalist categories
  78. #
  79. shalla_time="$(wget ${wget_parm} --timeout=5 --server-response --spider "${adb_arc_shalla}" 2>&1 | grep -F "Last-Modified: " 2>/dev/null | tr -d '\r' 2>/dev/null)"
  80. shalla_time="${shalla_time/*: /}"
  81. if [ -z "${shalla_time}" ]
  82. then
  83. shalla_time="$(date)"
  84. f_log " no online timestamp received, current date will be used"
  85. fi
  86. if [ -z "${list_time}" ] || [ "${list_time}" != "${shalla_time}" ]
  87. then
  88. wget ${wget_parm} --timeout="${adb_maxtime}" --output-document="${shalla_archive}" "${adb_arc_shalla}" 2>/dev/null
  89. rc=${?}
  90. if [ $((rc)) -eq 0 ]
  91. then
  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 " archive extraction failed (${category})"
  100. break
  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. if [ $((rc)) -eq 0 ]
  108. then
  109. adb_sources="${adb_sources} ${shalla_file}&ruleset=rset_shalla"
  110. f_log " source archive (pre-)processing finished"
  111. else
  112. rc=0
  113. fi
  114. else
  115. f_log " source archive download failed"
  116. rc=0
  117. fi
  118. else
  119. adb_srclist="! -name ${adb_dnsprefix}.${src_name}"
  120. f_log " source archive doesn't change, no update required"
  121. fi
  122. fi
  123. # add blacklist source to active adblock domain sources
  124. #
  125. if [ -s "${adb_blacklist}" ]
  126. then
  127. adb_sources="${adb_sources} ${adb_blacklist}&ruleset=rset_blacklist"
  128. fi
  129. # loop through active adblock domain sources,
  130. # download sources, prepare output and store all extracted domains in temp file
  131. #
  132. for src in ${adb_sources}
  133. do
  134. url="${src/\&ruleset=*/}"
  135. src_name="${src/*\&ruleset=rset_/}"
  136. adb_dnsfile="${adb_dnsdir}/${adb_dnsprefix}.${src_name}"
  137. list_time="$(grep -F "# last modified: " "${adb_dnsfile}" 2>/dev/null)"
  138. list_time="${list_time/*: /}"
  139. f_log "=> processing adblock source '${src_name}'"
  140. # prepare find statement with active adblock list sources
  141. #
  142. if [ -z "${adb_srclist}" ]
  143. then
  144. adb_srclist="! -name ${adb_dnsprefix}.${src_name}"
  145. else
  146. adb_srclist="${adb_srclist} -a ! -name ${adb_dnsprefix}.${src_name}"
  147. fi
  148. # only download adblock list with newer/updated timestamp
  149. #
  150. if [ "${src_name}" = "blacklist" ]
  151. then
  152. url_time="$(date -r "${adb_blacklist}" 2>/dev/null)"
  153. elif [ "${src_name}" = "shalla" ]
  154. then
  155. url_time="${shalla_time}"
  156. else
  157. 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)"
  158. url_time="${url_time/*: /}"
  159. fi
  160. if [ -z "${url_time}" ]
  161. then
  162. url_time="$(date)"
  163. f_log " no online timestamp received, current date will be used"
  164. fi
  165. if [ -z "${list_time}" ] || [ "${list_time}" != "${url_time}" ]
  166. then
  167. if [ "${src_name}" = "blacklist" ]
  168. then
  169. tmp_domains="$(cat "${adb_blacklist}" 2>/dev/null)"
  170. rc=${?}
  171. elif [ "${src_name}" = "shalla" ]
  172. then
  173. tmp_domains="$(cat "${shalla_file}" 2>/dev/null)"
  174. rc=${?}
  175. else
  176. tmp_domains="$(wget ${wget_parm} --timeout="${adb_maxtime}" --output-document=- "${url}" 2>/dev/null)"
  177. rc=${?}
  178. fi
  179. else
  180. f_log " source doesn't change, no update required"
  181. continue
  182. fi
  183. # check download result and prepare domain output by regex patterns
  184. #
  185. if [ $((rc)) -eq 0 ] && [ -n "${tmp_domains}" ]
  186. then
  187. eval "$(printf "${src}" | sed 's/\(.*\&ruleset=\)/ruleset=\$/g')"
  188. count="$(printf "%s\n" "${tmp_domains}" | tr '[A-Z]' '[a-z]' | eval "${ruleset}" | tee "${adb_tmpfile}" | wc -l)"
  189. f_log " source download finished (${count} entries)"
  190. if [ "${src_name}" = "shalla" ]
  191. then
  192. rm -f "${shalla_file}" >/dev/null 2>&1
  193. fi
  194. unset tmp_domains
  195. elif [ $((rc)) -eq 0 ] && [ -z "${tmp_domains}" ]
  196. then
  197. f_log " empty source download finished"
  198. continue
  199. else
  200. rc=0
  201. f_log " source download failed"
  202. continue
  203. fi
  204. # remove whitelist domains, sort domains and make them unique,
  205. # finally rewrite ad/abuse domain information to separate dnsmasq files
  206. #
  207. if [ $((count)) -gt 0 ] && [ -n "${adb_tmpfile}" ]
  208. then
  209. if [ -s "${adb_whitelist}" ]
  210. then
  211. 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}"
  212. rc=${?}
  213. else
  214. sort "${adb_tmpfile}" 2>/dev/null | uniq -u 2>/dev/null | eval "${adb_dnsformat}" 2>/dev/null > "${adb_dnsfile}"
  215. rc=${?}
  216. fi
  217. # prepare find statement with revised adblock list sources
  218. #
  219. if [ -z "${adb_revsrclist}" ]
  220. then
  221. adb_revsrclist="-name ${adb_dnsprefix}.${src_name}"
  222. else
  223. adb_revsrclist="${adb_revsrclist} -o -name ${adb_dnsprefix}.${src_name}"
  224. fi
  225. # write preliminary adblock list footer
  226. #
  227. if [ $((rc)) -eq 0 ]
  228. then
  229. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  230. then
  231. count="$(($(wc -l < "${adb_dnsdir}/${adb_dnsprefix}.${src_name}") / 2))"
  232. else
  233. count="$(wc -l < "${adb_dnsdir}/${adb_dnsprefix}.${src_name}")"
  234. fi
  235. printf "%s\n" "#------------------------------------------------------------------" >> "${adb_dnsfile}"
  236. printf "%s\n" "# ${0##*/} (${adb_version}) - ${count} ad/abuse domains blocked" >> "${adb_dnsfile}"
  237. printf "%s\n" "# source: ${url}" >> "${adb_dnsfile}"
  238. printf "%s\n" "# last modified: ${url_time}" >> "${adb_dnsfile}"
  239. f_log " domain merging finished"
  240. else
  241. f_log " domain merging failed" "${rc}"
  242. f_restore
  243. fi
  244. else
  245. f_log " empty domain input received"
  246. continue
  247. fi
  248. done
  249. # remove old adblock lists and their backups
  250. #
  251. if [ -n "${adb_srclist}" ]
  252. then
  253. adb_rmfind="$(find "${adb_dnsdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}.*" \( ${adb_srclist} \) -print -exec rm -f "{}" \; 2>/dev/null)"
  254. if [ $((rc)) -eq 0 ] && [ -n "${adb_rmfind}" ]
  255. then
  256. f_log "no longer used adblock lists removed" "${rc}"
  257. elif [ $((rc)) -ne 0 ]
  258. then
  259. f_log "error during removal of old adblock lists" "${rc}"
  260. f_exit
  261. fi
  262. if [ "${backup_ok}" = "true" ]
  263. then
  264. find "${adb_backupdir}" -maxdepth 1 -type f -name "${adb_dnsprefix}.*" \( ${adb_srclist} \) -exec rm -f "{}" \; 2>/dev/null
  265. if [ $((rc)) -ne 0 ]
  266. then
  267. f_log "error during removal of old backups" "${rc}"
  268. f_exit
  269. fi
  270. fi
  271. else
  272. rm -f "${adb_dnsdir}/${adb_dnsprefix}."* >/dev/null 2>&1
  273. if [ "${backup_ok}" = "true" ]
  274. then
  275. rm -f "${adb_backupdir}/${adb_dnsprefix}."* >/dev/null 2>&1
  276. f_log "all available adblock lists and backups removed"
  277. else
  278. f_log "all available adblock lists removed"
  279. fi
  280. fi
  281. # make separate adblock lists unique
  282. #
  283. if [ $((adb_unique)) -eq 1 ]
  284. then
  285. if [ -n "${adb_revsrclist}" ]
  286. then
  287. f_log "remove duplicates in separate adblock lists"
  288. # generate a temporary, unique overall list
  289. #
  290. head -qn -4 "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null | sort -u 2>/dev/null > "${adb_dnsdir}/tmp.overall"
  291. # loop through all separate lists, ordered by size (ascending)
  292. #
  293. for list in $(ls -Sr "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null)
  294. do
  295. # check separate lists vs. overall list,
  296. # rewrite only duplicate entries back to separate lists
  297. #
  298. list="${list/*./}"
  299. sort "${adb_dnsdir}/tmp.overall" "${adb_dnsdir}/${adb_dnsprefix}.${list}" 2>/dev/null | uniq -d 2>/dev/null > "${adb_dnsdir}/tmp.${list}"
  300. # remove these entries from overall list,
  301. # rewrite only unique entries back to overall list
  302. #
  303. tmp_unique="$(sort "${adb_dnsdir}/tmp.overall" "${adb_dnsdir}/tmp.${list}" 2>/dev/null | uniq -u 2>/dev/null)"
  304. printf "%s\n" "${tmp_unique}" > "${adb_dnsdir}/tmp.overall"
  305. # write final adblocklist footer
  306. #
  307. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  308. then
  309. count="$(($(wc -l < "${adb_dnsdir}/tmp.${list}") / 2))"
  310. else
  311. count="$(wc -l < "${adb_dnsdir}/tmp.${list}")"
  312. fi
  313. printf "%s\n" "#------------------------------------------------------------------" >> "${adb_dnsdir}/tmp.${list}"
  314. printf "%s\n" "# ${0##*/} (${adb_version}) - ${count} ad/abuse domains blocked" >> "${adb_dnsdir}/tmp.${list}"
  315. tail -qn -2 "${adb_dnsdir}/$adb_dnsprefix.${list}" 2>/dev/null >> "${adb_dnsdir}/tmp.${list}"
  316. mv -f "${adb_dnsdir}/tmp.${list}" "${adb_dnsdir}/${adb_dnsprefix}.${list}" >/dev/null 2>&1
  317. done
  318. rm -f "${adb_dnsdir}/tmp.overall" >/dev/null 2>&1
  319. fi
  320. fi
  321. # get overall count
  322. #
  323. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  324. then
  325. adb_count="$(($(head -qn -4 "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null | wc -l) / 2))"
  326. else
  327. adb_count="$(head -qn -4 "${adb_dnsdir}/${adb_dnsprefix}."* 2>/dev/null | wc -l)"
  328. fi
  329. # restore adblock lists if overall count is null (i.e. all downloads failed)
  330. #
  331. if [ "${backup_ok}" = "true" ] && [ $((adb_count)) -eq 0 ]
  332. then
  333. f_restore
  334. fi
  335. # restart dnsmasq with newly generated or deleted adblock lists,
  336. # check dnsmasq startup afterwards
  337. #
  338. if [ -n "${adb_revsrclist}" ] || [ -n "${adb_rmfind}" ]
  339. then
  340. /etc/init.d/dnsmasq restart >/dev/null 2>&1
  341. sleep 2
  342. dns_status="$(ps 2>/dev/null | grep "[d]nsmasq" 2>/dev/null)"
  343. if [ -n "${dns_status}" ]
  344. then
  345. f_log "adblock lists with overall ${adb_count} domains loaded"
  346. else
  347. rc=105
  348. f_log "dnsmasq restart failed, please check 'logread' output" "${rc}"
  349. f_restore
  350. fi
  351. else
  352. f_log "adblock lists with overall ${adb_count} domains are still valid, no dnsmasq restart required"
  353. fi
  354. # create adblock list backups
  355. #
  356. if [ "${backup_ok}" = "true" ] && [ "$(printf "${adb_dnsdir}/${adb_dnsprefix}."*)" != "${adb_dnsdir}/${adb_dnsprefix}.*" ]
  357. then
  358. for file in ${adb_dnsdir}/${adb_dnsprefix}.*
  359. do
  360. filename="${file##*/}"
  361. if [ ! -f "${adb_backupdir}/${filename}" ] || [ "${file}" -nt "${adb_backupdir}/${filename}" ]
  362. then
  363. cp -pf "${file}" "${adb_backupdir}" 2>/dev/null
  364. rc=${?}
  365. if [ $((rc)) -ne 0 ]
  366. then
  367. f_log "error during backup of adblock list (${filename})" "${rc}"
  368. f_exit
  369. fi
  370. backup_done="true"
  371. fi
  372. done
  373. if [ "${backup_done}" = "true" ]
  374. then
  375. f_log "new adblock list backups generated"
  376. else
  377. f_log "adblock list backups are still valid, no new backups required"
  378. fi
  379. fi
  380. # remove temporary files and exit
  381. #
  382. f_exit