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.

615 lines
18 KiB

  1. #!/bin/sh
  2. # function library used by adblock-update.sh
  3. # written by Dirk Brenken (openwrt@brenken.org)
  4. # f_envload: load adblock environment
  5. #
  6. f_envload()
  7. {
  8. # source in openwrt function library
  9. #
  10. if [ -r "/lib/functions.sh" ]
  11. then
  12. . "/lib/functions.sh"
  13. else
  14. rc=110
  15. f_log "openwrt function library not found" "${rc}"
  16. f_exit
  17. fi
  18. # source in openwrt network library
  19. #
  20. if [ -r "/lib/functions/network.sh" ]
  21. then
  22. . "/lib/functions/network.sh"
  23. else
  24. rc=115
  25. f_log "openwrt network library not found" "${rc}"
  26. f_exit
  27. fi
  28. # set initial defaults,
  29. # may be overwritten by setting appropriate adblock config options in global section of /etc/config/adblock
  30. #
  31. adb_lanif="lan"
  32. adb_nullport="65535"
  33. adb_nullipv4="192.0.2.1"
  34. adb_nullipv6="::ffff:c000:0201"
  35. adb_whitelist="/etc/adblock/adblock.whitelist"
  36. adb_whitelist_rset="\$1 ~/^([A-Za-z0-9_-]+\.){1,}[A-Za-z]+/{print tolower(\$1)}"
  37. adb_forcedns=1
  38. # function to parse global section by callback
  39. #
  40. config_cb()
  41. {
  42. local type="${1}"
  43. if [ "${type}" = "adblock" ]
  44. then
  45. option_cb()
  46. {
  47. local option="${1}"
  48. local value="${2}"
  49. eval "${option}=\"${value}\""
  50. }
  51. else
  52. reset_cb
  53. fi
  54. }
  55. # function to parse 'service' and 'source' sections
  56. #
  57. parse_config()
  58. {
  59. local value opt section="${1}" options="adb_dir adb_src adb_src_rset adb_src_cat"
  60. config_get switch "${section}" "enabled"
  61. if [ "${switch}" = "1" ]
  62. then
  63. if [ "${section}" != "backup" ]
  64. then
  65. eval "adb_sources=\"${adb_sources} ${section}\""
  66. fi
  67. for opt in ${options}
  68. do
  69. config_get value "${section}" "${opt}"
  70. if [ -n "${value}" ]
  71. then
  72. eval "${opt}_${section}=\"${value}\""
  73. fi
  74. done
  75. fi
  76. }
  77. # check opkg availability
  78. #
  79. if [ -r "/var/lock/opkg.lock" ]
  80. then
  81. rc=-1
  82. f_log "adblock installation finished successfully, 'opkg' currently locked by package installer"
  83. f_exit
  84. fi
  85. # get list with all installed openwrt packages
  86. #
  87. pkg_list="$(opkg list-installed)"
  88. if [ -z "${pkg_list}" ]
  89. then
  90. rc=120
  91. f_log "empty openwrt package list" "${rc}"
  92. f_exit
  93. fi
  94. # load adblock config and start parsing functions
  95. #
  96. config_load adblock
  97. config_foreach parse_config service
  98. config_foreach parse_config source
  99. # check 'enabled' & 'version' config options
  100. #
  101. if [ -z "${adb_enabled}" ] || [ -z "${adb_cfgversion}" ] || [ "${adb_cfgversion}" != "${adb_scriptver%.*}" ]
  102. then
  103. rc=125
  104. f_log "no valid adblock configuration found, please merge latest changes from '/etc/config/adblock.opkg' manually" "${rc}"
  105. f_exit
  106. fi
  107. if [ $((adb_enabled)) -ne 1 ]
  108. then
  109. rc=-1
  110. f_log "adblock is currently disabled, please set adblock.global.adb_enabled=1' to use this service"
  111. f_exit
  112. fi
  113. # set more script defaults (can't be overwritten by adblock config options)
  114. #
  115. adb_minspace=12000
  116. adb_tmpfile="$(mktemp -tu)"
  117. adb_tmpdir="$(mktemp -p /tmp -d)"
  118. adb_dnsdir="/tmp/dnsmasq.d"
  119. adb_dnsprefix="adb_list"
  120. adb_fetch="/usr/bin/wget"
  121. unset adb_srclist adb_revsrclist adb_errsrclist
  122. # get lan ip addresses
  123. #
  124. network_get_ipaddr adb_ipv4 "${adb_lanif}"
  125. network_get_ipaddr6 adb_ipv6 "${adb_lanif}"
  126. if [ -z "${adb_ipv4}" ] && [ -z "${adb_ipv6}" ]
  127. then
  128. rc=135
  129. f_log "no valid IPv4/IPv6 configuration found (${adb_lanif}), please set 'adb_lanif' manually" "${rc}"
  130. f_exit
  131. fi
  132. }
  133. # f_envcheck: check/set environment prerequisites
  134. #
  135. f_envcheck()
  136. {
  137. local check
  138. # check logical update interfaces (with default route)
  139. #
  140. network_find_wan adb_wanif4
  141. network_find_wan6 adb_wanif6
  142. if [ -z "${adb_wanif4}" ] && [ -z "${adb_wanif6}" ]
  143. then
  144. adb_wanif4="true"
  145. f_log "no valid IPv4/IPv6 interface with default route found, IPv4 mode will be assumed"
  146. fi
  147. # check general package dependencies
  148. #
  149. f_depend "uhttpd"
  150. f_depend "wget"
  151. f_depend "iptables"
  152. f_depend "kmod-ipt-nat"
  153. # check ipv6 related package dependencies
  154. #
  155. if [ -n "${adb_wanif6}" ]
  156. then
  157. check="$(printf "${pkg_list}" | grep "^ip6tables -")"
  158. if [ -z "${check}" ]
  159. then
  160. f_log "package 'ip6tables' not found, IPv6 support will be disabled"
  161. unset adb_wanif6
  162. else
  163. check="$(printf "${pkg_list}" | grep "^kmod-ipt-nat6 -")"
  164. if [ -z "${check}" ]
  165. then
  166. f_log "package 'kmod-ipt-nat6' not found, IPv6 support will be disabled"
  167. unset adb_wanif6
  168. fi
  169. fi
  170. fi
  171. # check ca-certificates package and set fetch parms accordingly
  172. #
  173. fetch_parm="--no-config --quiet --tries=1 --no-cache --no-cookies --max-redirect=0 --dns-timeout=5 --connect-timeout=5 --read-timeout=5"
  174. check="$(printf "${pkg_list}" | grep "^ca-certificates -")"
  175. if [ -z "${check}" ]
  176. then
  177. fetch_parm="${fetch_parm} --no-check-certificate"
  178. fi
  179. # check adblock temp directory
  180. #
  181. if [ -n "${adb_tmpdir}" ] && [ -d "${adb_tmpdir}" ]
  182. then
  183. f_space "${adb_tmpdir}"
  184. if [ "${space_ok}" = "false" ]
  185. then
  186. if [ $((av_space)) -le 2000 ]
  187. then
  188. rc=140
  189. f_log "not enough free space in '${adb_tmpdir}' (avail. ${av_space} kb)" "${rc}"
  190. f_exit
  191. else
  192. f_log "not enough free space to handle all adblock list sources at once in '${adb_tmpdir}' (avail. ${av_space} kb)"
  193. fi
  194. fi
  195. else
  196. rc=145
  197. f_log "temp directory not found" "${rc}"
  198. f_exit
  199. fi
  200. # check memory
  201. #
  202. mem_total="$(awk '$1 ~ /^MemTotal/ {printf $2}' "/proc/meminfo")"
  203. mem_free="$(awk '$1 ~ /^MemFree/ {printf $2}' "/proc/meminfo")"
  204. mem_swap="$(awk '$1 ~ /^SwapTotal/ {printf $2}' "/proc/meminfo")"
  205. if [ $((mem_total)) -le 64000 ] && [ $((mem_swap)) -eq 0 ]
  206. then
  207. mem_ok="false"
  208. f_log "not enough free memory, overall sort processing will be disabled (total: ${mem_total}, free: ${mem_free}, swap: ${mem_swap})"
  209. else
  210. mem_ok="true"
  211. fi
  212. # check backup configuration
  213. #
  214. if [ -n "${adb_dir_backup}" ] && [ -d "${adb_dir_backup}" ]
  215. then
  216. f_space "${adb_dir_backup}"
  217. if [ "${space_ok}" = "false" ]
  218. then
  219. f_log "not enough free space in '${adb_dir_backup}'(avail. ${av_space} kb), backup/restore will be disabled"
  220. backup_ok="false"
  221. else
  222. f_log "backup/restore will be enabled"
  223. backup_ok="true"
  224. fi
  225. else
  226. backup_ok="false"
  227. f_log "backup/restore will be disabled"
  228. fi
  229. # check running dnsmasq instance & set defaults
  230. #
  231. rc="$(ps | grep -q "[d]nsmasq"; printf ${?})"
  232. if [ $((rc)) -eq 0 ]
  233. then
  234. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  235. then
  236. adb_dnsformat="awk -v ipv4="${adb_nullipv4}" -v ipv6="${adb_nullipv6}" '{print \"address=/\"\$0\"/\"ipv4\"\n\"\"address=/\"\$0\"/\"ipv6}'"
  237. elif [ -n "${adb_wanif4}" ]
  238. then
  239. adb_dnsformat="awk -v ipv4="${adb_nullipv4}" '{print \"address=/\"\$0\"/\"ipv4}'"
  240. else
  241. adb_dnsformat="awk -v ipv6="${adb_nullipv6}" '{print \"address=/\"\$0\"/\"ipv6}'"
  242. fi
  243. else
  244. rc=150
  245. f_log "please enable the local dns server to use adblock" "${rc}"
  246. f_exit
  247. fi
  248. # check running firewall
  249. #
  250. check="$(/usr/sbin/iptables -vnL | grep -F "DROP")"
  251. if [ -z "${check}" ]
  252. then
  253. rc=155
  254. f_log "please enable the local firewall to use adblock" "${rc}"
  255. f_exit
  256. fi
  257. # check ipv4/iptables configuration
  258. #
  259. if [ -n "${adb_wanif4}" ]
  260. then
  261. f_firewall "IPv4" "nat" "A" "prerouting_rule" "adb-nat" "-p tcp -d ${adb_nullipv4} -m multiport --dports 80,443 -j DNAT --to-destination ${adb_ipv4}:${adb_nullport}"
  262. f_firewall "IPv4" "filter" "A" "forwarding_rule" "adb-fwd" "-p tcp -d ${adb_nullipv4} -j REJECT --reject-with tcp-reset"
  263. f_firewall "IPv4" "filter" "A" "forwarding_rule" "adb-fwd" "-d ${adb_nullipv4} -j REJECT --reject-with icmp-host-unreachable"
  264. f_firewall "IPv4" "filter" "A" "output_rule" "adb-out" "-p tcp -d ${adb_nullipv4} -j REJECT --reject-with tcp-reset"
  265. f_firewall "IPv4" "filter" "A" "output_rule" "adb-out" "-d ${adb_nullipv4} -j REJECT --reject-with icmp-host-unreachable"
  266. if [ $((adb_forcedns)) -eq 1 ]
  267. then
  268. f_firewall "IPv4" "nat" "A" "prerouting_rule" "adb-dns" "-p udp --dport 53 -j DNAT --to-destination ${adb_ipv4}:53"
  269. f_firewall "IPv4" "nat" "A" "prerouting_rule" "adb-dns" "-p tcp --dport 53 -j DNAT --to-destination ${adb_ipv4}:53"
  270. fi
  271. if [ "${fw_done}" = "true" ]
  272. then
  273. f_log "created volatile IPv4 firewall ruleset"
  274. fw_done="false"
  275. fi
  276. fi
  277. # check ipv6/ip6tables configuration
  278. #
  279. if [ -n "${adb_wanif6}" ]
  280. then
  281. f_firewall "IPv6" "nat" "A" "PREROUTING" "adb-nat" "-p tcp -d ${adb_nullipv6} -m multiport --dports 80,443 -j DNAT --to-destination [${adb_ipv6}]:${adb_nullport}"
  282. f_firewall "IPv6" "filter" "A" "forwarding_rule" "adb-fwd" "-p tcp -d ${adb_nullipv6} -j REJECT --reject-with tcp-reset"
  283. f_firewall "IPv6" "filter" "A" "forwarding_rule" "adb-fwd" "-d ${adb_nullipv6} -j REJECT --reject-with icmp6-addr-unreachable"
  284. f_firewall "IPv6" "filter" "A" "output_rule" "adb-out" "-p tcp -d ${adb_nullipv6} -j REJECT --reject-with tcp-reset"
  285. f_firewall "IPv6" "filter" "A" "output_rule" "adb-out" "-d ${adb_nullipv6} -j REJECT --reject-with icmp6-addr-unreachable"
  286. if [ $((adb_forcedns)) -eq 1 ]
  287. then
  288. f_firewall "IPv6" "nat" "A" "PREROUTING" "adb-dns" "-p udp --dport 53 -j DNAT --to-destination [${adb_ipv6}]:53"
  289. f_firewall "IPv6" "nat" "A" "PREROUTING" "adb-dns" "-p tcp --dport 53 -j DNAT --to-destination [${adb_ipv6}]:53"
  290. fi
  291. if [ "${fw_done}" = "true" ]
  292. then
  293. f_log "created volatile IPv6 firewall ruleset"
  294. fw_done="false"
  295. fi
  296. fi
  297. # check volatile adblock uhttpd instance configuration
  298. #
  299. rc="$(ps | grep -q "[u]httpd.*\-h /www/adblock"; printf ${?})"
  300. if [ $((rc)) -ne 0 ]
  301. then
  302. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  303. then
  304. uhttpd -h "/www/adblock" -k 0 -N 100 -t 0 -T 1 -D -S -E "/index.html" -p "${adb_ipv4}:${adb_nullport}" -p "[${adb_ipv6}]:${adb_nullport}"
  305. rc=${?}
  306. elif [ -n "${adb_wanif4}" ]
  307. then
  308. uhttpd -h "/www/adblock" -k 0 -N 100 -t 0 -T 1 -D -S -E "/index.html" -p "${adb_ipv4}:${adb_nullport}"
  309. rc=${?}
  310. else
  311. uhttpd -h "/www/adblock" -k 0 -N 100 -t 0 -T 1 -D -S -E "/index.html" -p "[${adb_ipv6}]:${adb_nullport}"
  312. rc=${?}
  313. fi
  314. if [ $((rc)) -eq 0 ]
  315. then
  316. f_log "created volatile uhttpd instance"
  317. else
  318. f_log "failed to initialize volatile uhttpd instance" "${rc}"
  319. f_restore
  320. fi
  321. fi
  322. # check whitelist entries
  323. #
  324. if [ -s "${adb_whitelist}" ]
  325. then
  326. awk "${adb_whitelist_rset}" "${adb_whitelist}" > "${adb_tmpdir}/tmp.whitelist"
  327. fi
  328. # remove no longer used opkg package list
  329. #
  330. unset pkg_list
  331. }
  332. # f_depend: check package dependencies
  333. #
  334. f_depend()
  335. {
  336. local check
  337. local package="${1}"
  338. check="$(printf "${pkg_list}" | grep "^${package} -")"
  339. if [ -z "${check}" ]
  340. then
  341. rc=160
  342. f_log "package '${package}' not found" "${rc}"
  343. f_exit
  344. fi
  345. }
  346. # f_firewall: set iptables rules for ipv4/ipv6
  347. #
  348. f_firewall()
  349. {
  350. local ipt
  351. local iptv4="/usr/sbin/iptables"
  352. local iptv6="/usr/sbin/ip6tables"
  353. local proto="${1}"
  354. local table="${2}"
  355. local ctype="${3}"
  356. local chain="${4}"
  357. local notes="${5}"
  358. local rules="${6}"
  359. # select appropriate iptables executable
  360. #
  361. if [ "${proto}" = "IPv4" ]
  362. then
  363. ipt="${iptv4}"
  364. else
  365. ipt="${iptv6}"
  366. fi
  367. # check whether iptables rule already applied and proceed accordingly
  368. #
  369. rc="$("${ipt}" -w -t "${table}" -C "${chain}" -m comment --comment "${notes}" ${rules}; printf ${?})"
  370. if [ $((rc)) -ne 0 ]
  371. then
  372. "${ipt}" -w -t "${table}" -"${ctype}" "${chain}" -m comment --comment "${notes}" ${rules}
  373. rc=${?}
  374. if [ $((rc)) -eq 0 ]
  375. then
  376. fw_done="true"
  377. else
  378. f_log "failed to initialize volatile ${proto} firewall rule '${notes}'" "${rc}"
  379. f_exit
  380. fi
  381. fi
  382. }
  383. # f_log: log messages to stdout and syslog
  384. #
  385. f_log()
  386. {
  387. local log_parm
  388. local log_msg="${1}"
  389. local log_rc="${2}"
  390. local class="info "
  391. # check for terminal session
  392. #
  393. if [ -t 1 ]
  394. then
  395. log_parm="-s"
  396. fi
  397. # log to different output devices and set log class accordingly
  398. #
  399. if [ -n "${log_msg}" ]
  400. then
  401. if [ $((log_rc)) -gt 0 ]
  402. then
  403. class="error"
  404. log_rc=", rc: ${log_rc}"
  405. log_msg="${log_msg}${log_rc}"
  406. fi
  407. /usr/bin/logger ${log_parm} -t "adblock[${adb_pid}] ${class}" "${log_msg}" 2>&1
  408. fi
  409. }
  410. ################################################
  411. # f_space: check mount points/space requirements
  412. #
  413. f_space()
  414. {
  415. local mp="${1}"
  416. if [ -d "${mp}" ]
  417. then
  418. av_space="$(df "${mp}" | tail -n1 | awk '{printf $4}')"
  419. if [ $((av_space)) -lt $((adb_minspace)) ]
  420. then
  421. space_ok="false"
  422. fi
  423. fi
  424. }
  425. # f_cntconfig: calculate counters in config
  426. #
  427. f_cntconfig()
  428. {
  429. local list
  430. local src_name
  431. local count=0
  432. local count_sum=0
  433. for list in $(ls -ASr "${adb_dnsdir}/${adb_dnsprefix}."*)
  434. do
  435. src_name="${list/*./}"
  436. count="$(wc -l < "${list}")"
  437. if [ -n "${adb_wanif4}" ] && [ -n "${adb_wanif6}" ]
  438. then
  439. count=$((count / 2))
  440. fi
  441. uci_set "adblock" "${src_name}" "adb_src_count" "${count}"
  442. count_sum=$((count_sum + count))
  443. done
  444. uci_set "adblock" "global" "adb_overall_count" "${count_sum}"
  445. }
  446. # f_rmconfig: remove counters & timestamps in given config sections
  447. #
  448. f_rmconfig()
  449. {
  450. local rm_done="${1}"
  451. for list in ${rm_done}
  452. do
  453. src_name="${list/*./}"
  454. if [ -n "${restore_done}" ]
  455. then
  456. uci_set "adblock" "${src_name}" "adb_src_timestamp" "list restored"
  457. else
  458. uci_remove "adblock" "${src_name}" "adb_src_count"
  459. uci_remove "adblock" "${src_name}" "adb_src_timestamp"
  460. fi
  461. done
  462. unset restore_done
  463. }
  464. # f_restore: restore last adblock list backups and restart dnsmasq
  465. #
  466. f_restore()
  467. {
  468. local rm_done
  469. local restore_done
  470. # remove bogus adblock lists
  471. #
  472. if [ -n "${adb_revsrclist}" ]
  473. then
  474. rm_done="$(find "${adb_dnsdir}" -maxdepth 1 -type f \( ${adb_revsrclist} \) -print -exec rm -f "{}" \;)"
  475. rc=${?}
  476. if [ $((rc)) -eq 0 ] && [ -n "${rm_done}" ]
  477. then
  478. f_rmconfig "${rm_done}"
  479. f_log "all bogus adblock lists removed"
  480. elif [ $((rc)) -ne 0 ]
  481. then
  482. f_log "error during removal of bogus adblock lists" "${rc}"
  483. f_exit
  484. fi
  485. fi
  486. # restore backups
  487. #
  488. if [ "${backup_ok}" = "true" ]
  489. then
  490. restore_done="$(find "${adb_dir_backup}" -maxdepth 1 -type f -name "${adb_dnsprefix}.*" -print -exec cp -pf "{}" "${adb_dnsdir}" \;)"
  491. rc=${?}
  492. if [ $((rc)) -eq 0 ] && [ -n "${restore_done}" ]
  493. then
  494. f_log "all available backups restored"
  495. elif [ $((rc)) -ne 0 ] && [ -n "${restore_done}" ]
  496. then
  497. f_log "error during restore of adblock lists" "${rc}"
  498. f_exit
  499. fi
  500. else
  501. f_log "backup service disabled, nothing to restore"
  502. fi
  503. # (re-)try dnsmasq restart without bogus adblock lists / with backups
  504. #
  505. if [ -n "${restore_done}" ] || [ -n "${rm_done}" ]
  506. then
  507. /etc/init.d/dnsmasq restart
  508. sleep 1
  509. rc="$(ps | grep -q "[d]nsmasq"; printf ${?})"
  510. if [ $((rc)) -eq 0 ]
  511. then
  512. rc=0
  513. f_cntconfig
  514. f_log "adblock lists with overall ${adb_count} domains loaded"
  515. else
  516. rc=165
  517. f_log "dnsmasq restart failed, please check 'logread' output" "${rc}"
  518. fi
  519. fi
  520. f_exit
  521. }
  522. # f_exit: delete (temporary) files, generate statistics and exit
  523. #
  524. f_exit()
  525. {
  526. local ipv4_adblock=0
  527. local ipv6_adblock=0
  528. local iptv4="/usr/sbin/iptables"
  529. local iptv6="/usr/sbin/ip6tables"
  530. # delete temporary files & directories
  531. #
  532. if [ -f "${adb_tmpfile}" ]
  533. then
  534. rm -f "${adb_tmpfile}"
  535. fi
  536. if [ -d "${adb_tmpdir}" ]
  537. then
  538. rm -rf "${adb_tmpdir}"
  539. fi
  540. # final log message and iptables statistics
  541. #
  542. if [ $((rc)) -eq 0 ]
  543. then
  544. if [ -n "${adb_wanif4}" ]
  545. then
  546. ipv4_adblock="$(${iptv4} -t nat -vnL | awk '$11 ~ /^adb-nat$/ {sum += $1} END {printf sum}')"
  547. ipv4_adblock="$((${ipv4_adblock} + $(${iptv4} -vnL | awk '$11 ~ /^adb-(fwd|out)$/ {sum += $1} END {printf sum}')))"
  548. fi
  549. if [ -n "${adb_wanif6}" ]
  550. then
  551. ipv6_adblock="$(${iptv6} -t nat -vnL | awk '$10 ~ /^adb-nat$/ {sum += $1} END {printf sum}')"
  552. ipv6_adblock="$((${ipv6_adblock} + $(${iptv6} -vnL | awk '$10 ~ /^adb-(fwd|out)$/ {sum += $1} END {printf sum}')))"
  553. fi
  554. if [ -n "$(uci changes adblock)" ]
  555. then
  556. uci_commit "adblock"
  557. fi
  558. f_log "firewall statistics (IPv4/IPv6): ${ipv4_adblock}/${ipv6_adblock} ad related packets blocked"
  559. f_log "domain adblock processing finished successfully (${adb_scriptver}, ${openwrt_version}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  560. elif [ $((rc)) -gt 0 ]
  561. then
  562. f_log "domain adblock processing failed (${adb_scriptver}, ${openwrt_version}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  563. else
  564. rc=0
  565. fi
  566. rm -f "${adb_pidfile}"
  567. exit ${rc}
  568. }