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
|
#!/bin/bash
# SUBROUTINES
## Present messages at varying verbosity levels:
### Level 0: Only fatal errors
### Level 1: Level 0 + non-fatal errors
### Level 2: Level 1 + updates to cache files
### Level 3: Level 2 + narration of each major phase
### Level 4: Level 3 + step-by-step details of all processes
### Level 5: Level 4 + stdout/stderr from sub-processes like curl, zip, 7za, etc.
_notify() {
case $1 in
0) _level="[FATAL]" ;;
1) _level="[WARN]" ;;
2) _level="[NOTE]" ;;
3) _level="[INFO]" ;;
4) _level="[DETAIL]" ;;
5) _level="[DEBUG]" ;;
esac
if [ $verbosity -ge $1 ]; then
echo -e "${_level} $2"
else
true
fi
}
## Report counts of addresses from a given hosts-like file
_count_hosts() {
grep -ah -- "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" "$@" | cut -d" " -f1 | sort -u | while read _addr; do
_number=$(grep -c -- "^$_addr" "$@")
_notify 3 "$@: $_number urls redirected to $_addr."
done
}
## Backup and/or recycle existing hostsfile
_backup_old() {
if [ $recycle_old == 1 ] || [ "$recycle_old" == "1" ] || [ "$recycle_old" == "yes" ] || [ "$recycle_old" == "true" ]; then
_notify 3 "Recycling old $hostsfile into new version..."
sort -u "$hostsfile" | sed "s|$| ! /etc/hosts.block.old|g" > "$tmpdir"/hostsblock/hosts.block.d/hosts.block.old && \
_notify 3 "Recycled old $hostsfile into new version." || \
_notify 1 "FAILED to recycle old $hostsfile into new version."
else
_notify 3 "Old $hostsfile will not be recycled into new version."
fi
if [ $backup_old == 0 ] || [ "$backup_old" == "0" ] && [ "$backup_old" == "no" ] && [ "$backup_old" == "false" ]; then
_notify 4 "Old $hostfile will not be backed up."
else
_notify 4 "Backing up old version of $hostsfile..."
ls "$hostsfile".old* &>/dev/null && rm "$hostsfile".old*
cp $_v -f -- "$hostsfile" "$hostsfile".old && \
if [ $backup_old == 1 ] || [ "$backup_old" == "1" ] || [ "$backup_old" == "yes" ] || [ "$backup_old" == "true" ]; then
_notify 3 "Backed up old $hostsfile."
else
eval $backup_old $_v -- "$hostsfile".old && \
_notify 3 "Backed up and compressed old $hostsfile with $backup_old." || _notify 1 "FAILED to compress $hostsfile with $backup_old."
fi || \
_notify 1 "FAILED to backup $hostsfile."
fi
}
## Extract entries from cachefiles
_extract_entries() {
_notify 4 "Extracting entries from $_cachefile..."
[ -d "$_cachefile_dir" ] || mkdir $_v -- "$_cachefile_dir" && \
_notify 4 "Created directory $_cachefile_dir." || _notify 1 "FAILED to create directory $_cachefile_dir."
cd "$_cachefile_dir"
case "$_decompresser" in
none)
_compress_exit=0
_notify 4 "No need to decompress $_basename_cachefile."
cp $_v -- "$_cachefile" "$_cachefile_dir"/ && _notify 4 "Moved $_basename_cachefile to $_cachefile_dir." || \
_notify 1 "FAILED to move $_basename_cachefile to $_cachefile_dir."
;;
unzip)
unzip -B -o -j $_v_unzip -- "$_cachefile" && _compress_exit=0 || _compress_exit=1
[ $_compress_exit == 0 ] && _notify 3 "Unzipped $_basename_cachefile." || _notify 1 "FAILED to unzip $_basename_cachefile."
;;
7z*)
if [ $verbosity -le 4 ]; then
eval $_7zip_available e "$_cachefile" &>/dev/null && _compress_exit=0 || _compress_exit=1
else
eval $_7zip_available e "$_cachefile" && _compress_exit=0 || _compress_exit=1
fi
[ $_compress_exit == 0 ] && _notify 3 "Un7zipped $_basename_cachefile." || _notify 1 "FAILED to un7zip $_basename_cachefile."
;;
esac
if [ $_compress_exit == 0 ]; then
_target_hostsfile="$tmpdir/hostsblock/hosts.block.d/$_basename_cachefile.hosts"
_notify 4 "Extracting obvious entries from $_basename_cachefile..."
_cachefile_url=$(head -n1 "$cachedir"/"$_basename_cachefile".url)
if grep -rah -- "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" ./* | sed -e 's/[[:space:]][[:space:]]*/ /g' -e \
"s/\#.*//g" -e "s/[[:space:]]$//g" -e "s/$_notredirect/$redirecturl/g" | sort -u | grep -vf "$whitelist" | \
sed "s|$| \! $_cachefile_url|g" > "$_target_hostsfile"; then
_notify 4 "Extracted obvious entries from $_basename_cachefile."
if [ $verbosity -ge 4 ]; then
_count_hosts "$_target_hostsfile"
fi
else
_notify 1 "FAILED to extract any obvious entries from $_basename_cachefile."
fi
_notify 4 "Extracting less-obvious entries from $_basename_cachefile"
if grep -rahv "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" ./* | grep -v "^\." | grep -v "\.$" | grep -v "\*" |\
grep -v "\"" | grep -v "\$" | grep "[a-z]" | grep "\." | sed "s/^/$redirecturl /g" | sed -e 's/[[:space:]][[:space:]]*/ /g' \
-e "s/\#.*//g" -e "s/[[:space:]]$//g" | sort -u | grep -vf "$whitelist" | sed "s|$| \! $_cachefile_url|g" |\
>> "$_target_hostsfile"; then
_notify 4 "Extracted less-obvious entries from $_basename_cachefile."
if [ $verbosity -ge 4 ]; then
_count_hosts "$_target_hostsfile"
fi
else
_notify 1 "FAILED to extract any less-obvious entries from $_basename_cachefile."
fi
_notify 4 "Deleting $_cachefile_dir..."
cd "$tmpdir"/hostsblock && \
rm $_v -r -- "$_cachefile_dir" && _notify 4 "Deleted $_cachefile_dir." || _notify 1 "FAILED to delete $_cachefile_dir."
fi
}
# OVERRIDE VERBOSITY SETTING IF SPECIFIED ON THE COMMAND LINE
_verbosity_check() {
if [ $_verbosity_override ]; then
_notify 4 "Overriding verbosity from $verbosity to $_verbosity_override given from the command line."
verbosity=$_verbosity_override
fi
}
# SET VERBOSITY VARIABLES FOR SUB-PROCESSES
_set_subprocess_verbosity() {
if [ $verbosity -le 2 ]; then
_v=""
_v_curl="-s"
_v_unzip="-qq"
elif [ $verbosity -le 4 ]; then
_v=""
_v_curl="-s"
_v_unzip="-q"
else
_v="-v"
_v_curl="-v"
_v_unzip="-v"
fi
}
# PRELIMINARY CHECK FOR CORRECT PRIVALEDGES AND DEPENDENCIES
_check_root() {
if [ $(whoami) != "root" ]; then
_notify 0 "INSUFFICIENT PERMISSIONS. RUN AS ROOT OR VIA SUDO. EXITING."
exit 3
else
_notify 4 "Running as root, which is what we want."
fi
}
# MAKE SURE NECESSARY DEPENDENCIES ARE PRESENT
_check_depends() {
for _depends in $@; do
if which "$_depends" &>/dev/null; then
_notify 4 "$_depends found."
else
_notify 0 "MISSING REQUIRED DEPENDENCY $_depends. PLEASE INSTALL. EXITING."
exit 5
fi
done
}
# CHECK FOR OPTIONAL DECOMPRESSION DEPENDENCIES
_check_unzip() {
if which unzip &>/dev/null; then
_notify 4 "unzip found. Will use it to extract zip archives."
_unzip_available=1
else
_notify 1 "Dearchiver for zip NOT FOUND. Optional functions which use this format will be skipped."
_unzip_available=0
fi
}
_check_7z() {
if which 7za &>/dev/null; then
_notify 4 "7za found. Will use it to handle 7z archives."
_7zip_available="7za"
elif which 7z &>/dev/null; then
_notify 4 "7z found. Will use it to handle 7z archives."
_7zip_available="7z"
else
_notify 1 "Dearchiver for 7za NOT FOUND. Optional functions which use this format will be skipped."
_7zip_available=0
fi
}
# SOURCE CONFIG FILE
_source_configfile() {
if [ $_configfile ]; then
if [ -f "$_configfile" ]; then
. "$_configfile"
_notify 4 "Using configuration file $_configfile."
elif [ -f /etc/hostsblock/hostsblock.conf ]; then
. /etc/hostsblock/hostsblock.conf
_notify 1 "Configuration file $_configfile NOT FOUND, using /etc/hostsblock/hostsblock.conf."
else
_notify 1 "Both configuration files $_configfile and /etc/hostsblock/hostsblock.conf NOT FOUND, using defaults."
fi
elif [ -f /etc/hostsblock/hostsblock.conf ]; then
. /etc/hostsblock/hostsblock.conf
_notify 4 "Using configuration file /etc/hostsblock/hostsblock.conf."
else
_notify 1 "Configuration file /etc/hostsblock/hostsblock.conf NOT FOUND, using defaults."
fi
}
# REWRITE A GIVEN FILE SANS REGEX STATEMENT
_strip_entries() {
case "$2" in
*.gz)
which pigz &>/dev/null && \
pigz -dc "$2" | grep -v "$1" | pigz -c - > "$2".tmp || \
gzip -dc "$2" | grep -v "$1" | gzip -c - > "$2".tmp
;;
*)
grep -v "$1" "$2" > "$2".tmp && \
mv $_v -f -- "$2".tmp "$2"
;;
esac
}
# CHECK TO SEE IF GIVEN URL IS BLOCKED OR UNBLOCKED AND OFFER TO CHANGE THIS.
_check_url(){
_url_escaped=$(echo "$@" | sed "s/\./\\\./g")
case "$annotate" in
*.gz)
which pigz &>/dev/null && \
_matches=$(pigz -dc "$annotate" | grep " $_url_escaped ") || \
_matches=$(gzip -dc "$annotate" | grep " $_url_escaped ")
;;
*)
_matches=$(grep " $_url_escaped " "$annotate")
;;
esac
_block_matches=$(echo "$_matches" | grep -- "^$redirecturl" | sed "s/.* \!\(.*\)$/\1/g" | tr '\n' ',' | sed "s/,$//g")
_redirect_matches=$(echo "$_matches" | grep -v "^$redirecturl" | \
sed "s/^\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\) .* \! \(.*\)$/to \1 by blocklist \2/g" | tr '\n' ',' | sed "s/,$//g")
if [ $(echo "$_block_matches" | wc -w) -gt 0 ] || [ $(echo "$_redirect_matches" | wc -w) -gt 0 ]; then
[ $(echo "$_block_matches" | wc -w) -gt 0 ] && echo -e "\n'$@' \e[1;31mBLOCKED \e[0mby blocklist(s)${_block_matches}"
[ $(echo "$_redirect_matches" | wc -w) -gt 0 ] && echo -e "\n'$@' \e[1;33mREDIRECTED \e[0m$_redirect_matches"
echo -e "\t1) Unblock/unredirect just $@\n\t2) Unblock/unredirect all sites containing url $@\n\t3) Keep blocked/redirected"
read -p "1-3 (default: 3): " b
if [[ $b == 1 || "$b" == "1" ]]; then
echo "Unblocking just $@"
echo " $@" >> "$whitelist"
_strip_entries " $@ \!" "$annotate"
_strip_entries " $@$" "$blacklist"
_strip_entries " $@$" "$hostsfile"
_changed=1
elif [[ $b == 2 || "$b" == "2" ]]; then
echo "Unblocking all sites containing url $@"
echo "$@" >> "$whitelist"
_strip_entries "$@" "$annotate"
_strip_entries "$@" "$blacklist"
_strip_entries "$@" "$hostsfile"
_changed=1
fi
else
echo -e "\n'$@' \e[0;32mNOT BLOCKED/REDIRECTED\e[0m\n\t1) Block $@\n\t2) Block $@ and delete all whitelist url entries containing $@\n\t3) Keep unblocked (default)"
read -p "1-3 (default: 3): " c
if [[ $c == 1 || "$c" == "1" ]]; then
echo "Blocking $@"
echo "$@" >> "$blacklist"
case "$annotate" in
*.gz)
if which pigz &>/dev/null; then
pigz -dc "$annotate" > "$annotate".tmp
echo "$redirecturl $@ \! $blacklist" >> "$annotate".tmp
sort -u "$annotate".tmp | pigz -c - > "$annotate"
else
gzip -dc "$annotate" > "$annotate".tmp
echo "$redirecturl $@ \! $blacklist" >> "$annotate".tmp
sort -u "$annotate".tmp | gzip -c - > "$annotate"
fi
rm -f "$_v" -- "$annotate".tmp
;;
*)
echo "$redirecturl $@ \! $blacklist" >> "$annotate"
;;
esac &
_strip_entries "^$@$" "$whitelist" &
echo "$redirecturl $@" >> "$hostsfile" &
_changed=1
wait
elif [[ $c == 2 || "$c" == "2" ]]; then
echo "Blocking $@ and deleting all whitelist url entries containing $@"
echo "$@" >> "$blacklist" &
case "$annotate" in
*.gz)
if which pigz &>/dev/null; then
pigz -dc "$annotate" > "$annotate".tmp
echo "$redirecturl $@ \! $blacklist" >> "$annotate".tmp
sort -u "$annotate".tmp | pigz -c - > "$annotate"
else
gzip -dc "$annotate" > "$annotate".tmp
echo "$redirecturl $@ \! $blacklist" >> "$annotate".tmp
sort -u "$annotate".tmp | gzip -c - > "$annotate"
fi
rm -f "$_v" -- "$annotate".tmp
;;
*)
echo "$redirecturl $@ \! $blacklist" >> "$annotate"
;;
esac &
_strip_entries "$@" "$whitelist" &
echo "$redirecturl $@" >> "$hostsfile" &
_changed=1
fi
fi
}
# SET DEFAULT SETTINGS
export tmpdir="/dev/shm"
export hostsfile="/etc/hosts"
export redirecturl="127.0.0.1"
export dnscacher="auto"
postprocess() {
/bin/true
}
export blocklists=("http://support.it-mate.co.uk/downloads/HOSTS.txt")
export blacklist="/etc/hostsblock/black.list"
export whitelist="/etc/hostsblock/white.list"
export hostshead="0"
export cachedir="/var/cache/hostsblock"
export redirects="0"
export connect_timeout=60
export retry=0
if which pigz &>/dev/null; then
export backup_old="pigz"
elif which gzip &>/dev/null; then
export backup_old="gzip"
else
export backup_old=0
fi
export recycle_old=1
export verbosity=1
export annotate=/var/lib/hostsblock.db.gz
|