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