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.

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