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.

670 lines
19 KiB

  1. #!/bin/sh
  2. ##############################################
  3. # function library used by adblock-update.sh #
  4. # written by Dirk Brenken (dirk@brenken.org) #
  5. ##############################################
  6. #####################################
  7. # f_envload: load adblock environment
  8. #
  9. f_envload()
  10. {
  11. # source in openwrt function library
  12. #
  13. if [ -r "/lib/functions.sh" ]
  14. then
  15. . /lib/functions.sh
  16. else
  17. rc=510
  18. f_log "openwrt function library not found" "${rc}"
  19. f_deltemp
  20. fi
  21. # source in openwrt json helpers library
  22. #
  23. if [ -r "/usr/share/libubox/jshn.sh" ]
  24. then
  25. . "/usr/share/libubox/jshn.sh"
  26. else
  27. rc=515
  28. f_log "openwrt json helpers library not found" "${rc}"
  29. f_deltemp
  30. fi
  31. # get list with all installed openwrt packages
  32. #
  33. pkg_list="$(opkg list-installed 2>/dev/null)"
  34. if [ -z "${pkg_list}" ]
  35. then
  36. rc=520
  37. f_log "empty openwrt package list" "${rc}"
  38. f_deltemp
  39. fi
  40. }
  41. ######################################################
  42. # f_envparse: parse adblock config and set environment
  43. #
  44. f_envparse()
  45. {
  46. # set the C locale, characters are single bytes, the charset is ASCII
  47. # speeds up sort, grep etc., guarantees unique domains
  48. #
  49. LC_ALL=C
  50. # set initial defaults (may be overwritten by adblock config options)
  51. #
  52. adb_if="adblock"
  53. adb_minspace="20000"
  54. adb_maxtime="60"
  55. adb_maxloop="5"
  56. # adblock device name auto detection
  57. # derived from first entry in openwrt lan ifname config
  58. #
  59. adb_dev="$(uci get network.lan.ifname 2>/dev/null)"
  60. adb_dev="${adb_dev/ *}"
  61. # adblock ntp server name auto detection
  62. # derived from ntp list found in openwrt ntp server config
  63. #
  64. adb_ntpsrv="$(uci get system.ntp.server 2>/dev/null)"
  65. # function to read/set global options by callback,
  66. # prepare list items and build option list for all others
  67. #
  68. config_cb()
  69. {
  70. local type="${1}"
  71. local name="${2}"
  72. if [ "${type}" = "adblock" ]
  73. then
  74. option_cb()
  75. {
  76. local option="${1}"
  77. local value="${2}"
  78. eval "${option}=\"${value}\""
  79. }
  80. else
  81. option_cb()
  82. {
  83. local option="${1}"
  84. local value="${2}"
  85. local opt_out="$(printf "${option}" | sed -n '/.*_ITEM[0-9]$/p; /.*_LENGTH$/p; /enabled/p')"
  86. if [ -z "${opt_out}" ]
  87. then
  88. all_options="${all_options} ${option}"
  89. fi
  90. }
  91. list_cb()
  92. {
  93. local list="${1}"
  94. local value="${2}"
  95. if [ "${list}" = "adb_wanlist" ]
  96. then
  97. adb_wandev="${adb_wandev} ${value}"
  98. elif [ "${list}" = "adb_ntplist" ]
  99. then
  100. adb_ntpsrv="${adb_ntpsrv} ${value}"
  101. elif [ "${list}" = "adb_catlist" ]
  102. then
  103. adb_cat_shalla="${adb_cat_shalla} ${value}"
  104. fi
  105. }
  106. fi
  107. }
  108. # function to iterate through option list, read/set all options in "enabled" sections
  109. #
  110. parse_config()
  111. {
  112. local config="${1}"
  113. config_get switch "${config}" "enabled"
  114. if [ "${switch}" = "1" ]
  115. then
  116. for option in ${all_options}
  117. do
  118. config_get value "${config}" "${option}"
  119. if [ -n "${value}" ]
  120. then
  121. local opt_src="$(printf "${option}" | sed -n '/^adb_src_[a-z0-9]*$/p')"
  122. if [ -n "${opt_src}" ]
  123. then
  124. adb_sources="${adb_sources} ${value}"
  125. else
  126. eval "${option}=\"${value}\""
  127. fi
  128. fi
  129. done
  130. elif [ "${config}" = "wancheck" ]
  131. then
  132. unset adb_wandev 2>/dev/null
  133. elif [ "${config}" = "ntpcheck" ]
  134. then
  135. unset adb_ntpsrv 2>/dev/null
  136. elif [ "${config}" = "shalla" ]
  137. then
  138. unset adb_cat_shalla 2>/dev/null
  139. fi
  140. }
  141. # load adblock config and start parsing functions
  142. #
  143. config_load adblock
  144. config_foreach parse_config service
  145. config_foreach parse_config source
  146. # set temp variables and counter
  147. #
  148. adb_tmpfile="$(mktemp -tu 2>/dev/null)"
  149. adb_tmpdir="$(mktemp -p /tmp -d 2>/dev/null)"
  150. # set adblock source ruleset definitions
  151. #
  152. rset_start="sed -r 's/[[:space:]]|[\[!#/:;_].*|[0-9\.]*localhost//g; s/[\^#/:;_\.\t ]*$//g'"
  153. rset_end="sed '/^[#/:;_\s]*$/d'"
  154. rset_default="${rset_start} | ${rset_end}"
  155. rset_yoyo="${rset_start} | sed 's/,/\n/g' | ${rset_end}"
  156. rset_shalla="${rset_start} | sed 's/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}$//g' | ${rset_end}"
  157. rset_spam404="${rset_start} | sed 's/^\|\|//g' | ${rset_end}"
  158. rset_winhelp="${rset_start} | sed 's/\([0-9]\{1,3\}\.\)\{3\}[0-1]\{1,1\}//g' | ${rset_end}"
  159. # set adblock/dnsmasq destination file and format
  160. #
  161. adb_dnsfile="/tmp/dnsmasq.d/adlist.conf"
  162. adb_dnsformat="sed 's/^/address=\//;s/$/\/'${adb_ip}'/'"
  163. }
  164. #############################################
  165. # f_envcheck: check environment prerequisites
  166. #
  167. f_envcheck()
  168. {
  169. # check required config variables
  170. #
  171. adb_varlist="adb_ip adb_dev adb_if adb_domain adb_minspace adb_maxloop adb_maxtime adb_blacklist adb_whitelist"
  172. for var in ${adb_varlist}
  173. do
  174. if [ -z "$(eval printf \"\$"${var}"\")" ]
  175. then
  176. rc=525
  177. f_log "missing adblock config option (${var})" "${rc}"
  178. f_deltemp
  179. fi
  180. done
  181. # check main uhttpd configuration
  182. #
  183. check_uhttpd="$(uci get uhttpd.main.listen_http 2>/dev/null | grep -Fo "0.0.0.0")"
  184. if [ -n "${check_uhttpd}" ]
  185. then
  186. rc=530
  187. lan_ip="$(uci get network.lan.ipaddr 2>/dev/null)"
  188. f_log "main uhttpd instance listens to all network interfaces, please bind uhttpd to LAN only (${lan_ip})" "${rc}"
  189. f_deltemp
  190. fi
  191. # check adblock network device configuration
  192. #
  193. if [ ! -d "/sys/class/net/${adb_dev}" ]
  194. then
  195. rc=535
  196. f_log "invalid adblock network device input (${adb_dev})" "${rc}"
  197. f_deltemp
  198. fi
  199. # check adblock network interface configuration
  200. #
  201. check_if="$(printf "${adb_if}" | sed -n '/[^._0-9A-Za-z]/p')"
  202. banned_if="$(printf "${adb_if}" | sed -n '/.*lan.*\|.*wan.*\|.*switch.*\|main\|globals\|loopback\|px5g/p')"
  203. if [ -n "${check_if}" ] || [ -n "${banned_if}" ]
  204. then
  205. rc=540
  206. f_log "invalid adblock network interface input (${adb_if})" "${rc}"
  207. f_deltemp
  208. fi
  209. # check adblock ip address configuration
  210. #
  211. check_ip="$(printf "${adb_ip}" | sed -n '/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/p')"
  212. if [ -z "${check_ip}" ]
  213. then
  214. rc=545
  215. f_log "invalid adblock ip address input (${adb_ip})" "${rc}"
  216. f_deltemp
  217. fi
  218. # check adblock blacklist/whitelist configuration
  219. #
  220. if [ ! -r "${adb_blacklist}" ]
  221. then
  222. rc=550
  223. f_log "adblock blacklist not found" "${rc}"
  224. f_deltemp
  225. elif [ ! -r "${adb_whitelist}" ]
  226. then
  227. rc=555
  228. f_log "adblock whitelist not found" "${rc}"
  229. f_deltemp
  230. fi
  231. # check adblock temp directory
  232. #
  233. if [ -n "${adb_tmpdir}" ] && [ -d "${adb_tmpdir}" ]
  234. then
  235. f_space "${adb_tmpdir}"
  236. tmp_ok="true"
  237. else
  238. rc=560
  239. tmp_ok="false"
  240. f_log "temp directory not found" "${rc}"
  241. f_deltemp
  242. fi
  243. # check curl package dependency
  244. #
  245. check="$(printf "${pkg_list}" | grep "^curl -")"
  246. if [ -z "${check}" ]
  247. then
  248. rc=565
  249. f_log "curl package not found" "${rc}"
  250. f_deltemp
  251. fi
  252. # check wget package dependency
  253. #
  254. check="$(printf "${pkg_list}" | grep "^wget -")"
  255. if [ -z "${check}" ]
  256. then
  257. rc=570
  258. f_log "wget package not found" "${rc}"
  259. f_deltemp
  260. fi
  261. # check ca-certificates package and set wget/curl parms accordingly
  262. #
  263. check="$(printf "${pkg_list}" | grep "^ca-certificates -")"
  264. if [ -z "${check}" ]
  265. then
  266. curl_parm="-q --insecure"
  267. wget_parm="--no-config --no-hsts --no-check-certificate"
  268. else
  269. curl_parm="-q"
  270. wget_parm="--no-config --no-hsts"
  271. fi
  272. # check total and swap memory
  273. #
  274. mem_total="$(cat /proc/meminfo | grep -F "MemTotal" | grep -o "[0-9]*")"
  275. mem_free="$(cat /proc/meminfo | grep -F "MemFree" | grep -o "[0-9]*")"
  276. swap_total="$(cat /proc/meminfo | grep -F "SwapTotal" | grep -o "[0-9]*")"
  277. if [ $((mem_total)) -le 64000 ] && [ $((swap_total)) -eq 0 ]
  278. then
  279. f_log "please consider adding an external swap device to supersize your /tmp directory (total: ${mem_total}, free: ${mem_free}, swap: ${mem_swap})"
  280. fi
  281. # check backup configuration
  282. #
  283. adb_backupdir="${adb_backupfile%/*}"
  284. if [ -n "${adb_backupdir}" ] && [ -d "${adb_backupdir}" ]
  285. then
  286. f_space "${adb_backupdir}"
  287. backup_ok="true"
  288. else
  289. backup_ok="false"
  290. f_log "backup/restore will be disabled"
  291. fi
  292. # check dns query log configuration
  293. #
  294. adb_querydir="${adb_queryfile%/*}"
  295. adb_querypid="/var/run/adb_query.pid"
  296. if [ -n "${adb_querydir}" ] && [ -d "${adb_querydir}" ]
  297. then
  298. # check find capabilities
  299. #
  300. check="$(find --help 2>&1 | grep -F "mtime")"
  301. if [ -z "${check}" ]
  302. then
  303. query_ok="false"
  304. f_log "busybox without 'find/mtime' support (min. r47362), dns query logging will be disabled"
  305. else
  306. f_space "${adb_querydir}"
  307. query_ok="true"
  308. query_name="${adb_queryfile##*/}"
  309. query_ip="${adb_ip//./\\.}"
  310. fi
  311. else
  312. query_ok="false"
  313. f_log "dns query logging will be disabled"
  314. if [ -s "${adb_querypid}" ]
  315. then
  316. kill -9 "$(cat "${adb_querypid}")" >/dev/null 2>&1
  317. f_log "remove old dns query log background process (pid: $(cat "${adb_querypid}" 2>/dev/null))"
  318. > "${adb_querypid}"
  319. fi
  320. fi
  321. # check debug log configuration
  322. #
  323. adb_logdir="${adb_logfile%/*}"
  324. if [ -n "${adb_logdir}" ] && [ -d "${adb_logdir}" ]
  325. then
  326. f_space "${adb_logdir}"
  327. log_ok="true"
  328. else
  329. log_ok="false"
  330. f_log "debug logging will be disabled"
  331. fi
  332. # check wan update configuration
  333. #
  334. if [ -n "${adb_wandev}" ]
  335. then
  336. f_wancheck "${adb_maxloop}"
  337. else
  338. wan_ok="false"
  339. f_log "wan update check will be disabled"
  340. fi
  341. # check ntp sync configuration
  342. #
  343. if [ -n "${adb_ntpsrv}" ]
  344. then
  345. f_ntpcheck "${adb_maxloop}"
  346. else
  347. ntp_ok="false"
  348. f_log "ntp time sync will be disabled"
  349. fi
  350. # check dynamic/volatile adblock network interface configuration
  351. #
  352. rc="$(ifstatus "${adb_if}" >/dev/null 2>&1; printf ${?})"
  353. if [ $((rc)) -ne 0 ]
  354. then
  355. json_init
  356. json_add_string name "${adb_if}"
  357. json_add_string ifname "${adb_dev}"
  358. json_add_string proto "static"
  359. json_add_array ipaddr
  360. json_add_string "" "${adb_ip}"
  361. json_close_array
  362. json_close_object
  363. ubus call network add_dynamic "$(json_dump)"
  364. rc=${?}
  365. if [ $((rc)) -eq 0 ]
  366. then
  367. f_log "created new dynamic/volatile network interface (${adb_if}, ${adb_ip})"
  368. else
  369. f_log "failed to initialize new dynamic/volatile network interface (${adb_if}, ${adb_ip})" "${rc}"
  370. f_remove
  371. fi
  372. fi
  373. # check dynamic/volatile adblock uhttpd instance configuration
  374. #
  375. rc="$(ps | grep "[u]httpd.*\-r ${adb_if}" >/dev/null 2>&1; printf ${?})"
  376. if [ $((rc)) -ne 0 ]
  377. then
  378. uhttpd -h "/www/adblock" -r "${adb_if}" -E "/adblock.html" -p "${adb_ip}:80" >/dev/null 2>&1
  379. rc=${?}
  380. if [ $((rc)) -eq 0 ]
  381. then
  382. f_log "created new dynamic/volatile uhttpd instance (${adb_if}, ${adb_ip})"
  383. else
  384. f_log "failed to initialize new dynamic/volatile uhttpd instance (${adb_if}, ${adb_ip})" "${rc}"
  385. f_remove
  386. fi
  387. fi
  388. # remove no longer used environment variables
  389. #
  390. env_list="$(set | grep -o "CONFIG_[A-Za-z0-9_]*")"
  391. for var in ${env_list}
  392. do
  393. unset "${var}" 2>/dev/null
  394. done
  395. unset env_list 2>/dev/null
  396. unset pkg_list 2>/dev/null
  397. }
  398. ################################################
  399. # f_log: log messages to stdout, syslog, logfile
  400. #
  401. f_log()
  402. {
  403. local log_msg="${1}"
  404. local log_rc="${2}"
  405. local class="info "
  406. if [ -n "${log_msg}" ]
  407. then
  408. if [ $((log_rc)) -ne 0 ]
  409. then
  410. class="error"
  411. log_rc=", rc: ${log_rc}"
  412. log_msg="${log_msg}${log_rc}"
  413. fi
  414. /usr/bin/logger -s -t "adblock[${pid}] ${class}" "${log_msg}"
  415. if [ "${log_ok}" = "true" ] && [ "${ntp_ok}" = "true" ]
  416. then
  417. printf "%s\n" "$(/bin/date "+%d.%m.%Y %H:%M:%S") adblock[${pid}] ${class}: ${log_msg}" >> "${adb_logfile}"
  418. fi
  419. fi
  420. }
  421. ################################################
  422. # f_space: check mount points/space requirements
  423. #
  424. f_space()
  425. {
  426. local mp="${1}"
  427. if [ -d "${mp}" ]
  428. then
  429. df "${mp}" 2>/dev/null |\
  430. tail -n1 |\
  431. while read filesystem overall used available scrap
  432. do
  433. av_space="${available}"
  434. if [ $((av_space)) -eq 0 ]
  435. then
  436. rc=575
  437. f_log "no space left on device/not mounted (${mp})" "${rc}"
  438. exit ${rc}
  439. elif [ $((av_space)) -lt $((adb_minspace)) ]
  440. then
  441. rc=580
  442. f_log "not enough space left on device (${mp})" "${rc}"
  443. exit ${rc}
  444. fi
  445. done
  446. rc=${?}
  447. if [ $((rc)) -eq 0 ]
  448. then
  449. space_ok="true"
  450. else
  451. space_ok="false"
  452. f_deltemp
  453. fi
  454. fi
  455. }
  456. ####################################################
  457. # f_deltemp: delete temp files, directories and exit
  458. #
  459. f_deltemp()
  460. {
  461. if [ -f "${adb_tmpfile}" ]
  462. then
  463. rm -f "${adb_tmpfile}" >/dev/null 2>&1
  464. fi
  465. if [ -d "${adb_tmpdir}" ]
  466. then
  467. rm -rf "${adb_tmpdir}" >/dev/null 2>&1
  468. fi
  469. f_log "domain adblock processing finished (${adb_version}, ${openwrt_version}, $(/bin/date "+%d.%m.%Y %H:%M:%S"))"
  470. exit ${rc}
  471. }
  472. ####################################################
  473. # f_remove: maintain and (re-)start domain query log
  474. #
  475. f_remove()
  476. {
  477. if [ "${query_ok}" = "true" ] && [ "${ntp_ok}" = "true" ]
  478. then
  479. query_date="$(date "+%Y%m%d")"
  480. if [ -s "${adb_querypid}" ] && [ ! -f "${adb_queryfile}.${query_date}" ]
  481. then
  482. kill -9 "$(cat "${adb_querypid}")" >/dev/null 2>&1
  483. find "${adb_backupdir}" -maxdepth 1 -type f -mtime +"${adb_queryhistory}" -name "${query_name}.*" -exec rm -f {} \; 2>/dev/null
  484. f_log "remove old domain query log background process (pid: $(cat "${adb_querypid}")) and do logfile housekeeping"
  485. > "${adb_querypid}"
  486. fi
  487. if [ ! -s "${adb_querypid}" ]
  488. then
  489. (logread -f 2>/dev/null & printf ${!} > "${adb_querypid}") | grep -Eo "(query\[A\].*)|([a-z0-9\.\-]* is ${query_ip}$)" >> "${adb_queryfile}.${query_date}" &
  490. f_log "new domain query log background process started"
  491. fi
  492. fi
  493. f_deltemp
  494. }
  495. ################################################################
  496. # f_restore: restore last adblocklist backup and restart dnsmasq
  497. #
  498. f_restore()
  499. {
  500. if [ "${backup_ok}" = "true" ] && [ -f "${adb_backupfile}" ]
  501. then
  502. cp -f "${adb_backupfile}" "${adb_dnsfile}" >/dev/null 2>&1
  503. f_log "adblocklist backup restored"
  504. else
  505. > "${adb_dnsfile}"
  506. f_log="empty adblocklist generated"
  507. fi
  508. /etc/init.d/dnsmasq restart >/dev/null 2>&1
  509. f_remove
  510. }
  511. #######################################################
  512. # f_wancheck: check for usable adblock update interface
  513. #
  514. f_wancheck()
  515. {
  516. local cnt=0
  517. local cnt_max="${1}"
  518. local dev
  519. local dev_out
  520. while [ $((cnt)) -le $((cnt_max)) ]
  521. do
  522. for dev in ${adb_wandev}
  523. do
  524. if [ -d "/sys/class/net/${dev}" ]
  525. then
  526. dev_out="$(cat /sys/class/net/${dev}/operstate 2>/dev/null)"
  527. rc=${?}
  528. if [ "${dev_out}" = "up" ]
  529. then
  530. wan_ok="true"
  531. f_log "get wan/update interface (${dev}), after ${cnt} loops"
  532. break 2
  533. fi
  534. fi
  535. done
  536. sleep 1
  537. cnt=$((cnt + 1))
  538. done
  539. if [ -z "${wan_ok}" ]
  540. then
  541. rc=585
  542. wan_ok="false"
  543. f_log "no wan/update interface(s) found (${adb_wandev# })" "${rc}"
  544. f_restore
  545. fi
  546. }
  547. #####################################
  548. # f_ntpcheck: check/get ntp time sync
  549. #
  550. f_ntpcheck()
  551. {
  552. local cnt=0
  553. local cnt_max="${1}"
  554. local ntp_pool
  555. for srv in ${adb_ntpsrv}
  556. do
  557. ntp_pool="${ntp_pool} -p ${srv}"
  558. done
  559. while [ $((cnt)) -le $((cnt_max)) ]
  560. do
  561. /usr/sbin/ntpd -nq ${ntp_pool} >/dev/null 2>&1
  562. rc=${?}
  563. if [ $((rc)) -eq 0 ]
  564. then
  565. ntp_ok="true"
  566. f_log "get ntp time sync (${adb_ntpsrv# }), after ${cnt} loops"
  567. break
  568. fi
  569. sleep 1
  570. cnt=$((cnt + 1))
  571. done
  572. if [ -z "${ntp_ok}" ]
  573. then
  574. rc=590
  575. ntp_ok="false"
  576. f_log "ntp time sync failed (${adb_ntpsrv# })" "${rc}"
  577. f_restore
  578. fi
  579. }
  580. ####################################################################
  581. # f_dnscheck: dnsmasq health check with newly generated adblock list
  582. #
  583. f_dnscheck()
  584. {
  585. local dns_status
  586. dns_status="$(logread -l 20 -e "dnsmasq" -e "FAILED to start up")"
  587. rc=${?}
  588. if [ -z "${dns_status}" ]
  589. then
  590. dns_status="$(nslookup "${adb_domain}" 2>/dev/null | grep -F "${adb_ip}")"
  591. rc=${?}
  592. if [ -z "${dns_status}" ]
  593. then
  594. if [ "${backup_ok}" = "true" ]
  595. then
  596. cp -f "${adb_dnsfile}" "${adb_backupfile}" >/dev/null 2>&1
  597. f_log "new adblock list with ${adb_count} domains loaded, backup generated"
  598. else
  599. f_log "new adblock list with ${adb_count} domains loaded, no backup"
  600. fi
  601. else
  602. f_log "nslookup probe failed" "${rc}"
  603. f_restore
  604. fi
  605. else
  606. f_log "dnsmasq probe failed" "${rc}"
  607. f_restore
  608. fi
  609. }
  610. ##########################################################
  611. # f_footer: write footer with a few statistics to dns file
  612. #
  613. f_footer()
  614. {
  615. local url
  616. adb_count="$(wc -l < "${adb_dnsfile}")"
  617. printf "%s\n" "####################################################" >> "${adb_dnsfile}"
  618. printf "%s\n" "# last adblock list update: $(date +"%d.%m.%Y - %T")" >> "${adb_dnsfile}"
  619. printf "%s\n" "# ${0##*/} (${adb_version}) - ${adb_count} ad/abuse domains blocked" >> "${adb_dnsfile}"
  620. printf "%s\n" "# domain blacklist sources:" >> "${adb_dnsfile}"
  621. for src in ${adb_sources}
  622. do
  623. url="${src//\&ruleset=*/}"
  624. printf "%s\n" "# ${url}" >> "${adb_dnsfile}"
  625. done
  626. printf "%s\n" "#####" >> "${adb_dnsfile}"
  627. printf "%s\n" "# ${adb_whitelist}" >> "${adb_dnsfile}"
  628. printf "%s\n" "####################################################" >> "${adb_dnsfile}"
  629. }