-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaxilog
More file actions
513 lines (434 loc) · 15.3 KB
/
maxilog
File metadata and controls
513 lines (434 loc) · 15.3 KB
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
#!/bin/bash
# ==================================================
_APP_SPECIFIC_NAME="MaxiLog"
_APP_VERSION="0.2"
_APP_STATUS="beta"
_APP_INFO="${_APP_SPECIFIC_NAME} (maxilog) is bash script use to scan IP address inside logs"
_APP_VERSION_STATUS="${_APP_VERSION}-${_APP_STATUS}"
_AUTHOR="Author: Arafat Ali | Email: arafat@sofibox.com | (C) 2019-2023"
# ====================================================
# This function is used to update script from a remote repository based on a given path
# Usage: script_update [script_path]
# Example: script_update /opt/maxigpg_public/
script_update() {
local script_name script_path caller script_install_path current_configs new_configs
script_name="$(basename "${0}")"
# might not work with macOS
script_path="$(dirname "$(readlink -f "$0")")"
caller="${script_name}->${FUNCNAME[0]}"
script_install_path="${1}"
echo "[${caller}]: Updating $(basename -- "$0") to latest version ..."
# CD to ${script_path} and if does not exist exit with error
cd "${script_path}" || {
echo "[${caller}]: ERROR, could not change directory to ${script_path}"
exit 1
}
echo ""
echo "START git update information:"
git fetch --all
check_status "$?"
git reset --hard origin/main
check_status "$?"
echo "END git update information:"
echo ""
echo "[${caller}]: Updating ${script_name} configuration file ..."
current_configs=$(grep -E '^[A-Za-z0-9_].+=.+$' "${script_install_path}/${script_name}.conf" | awk -F "=" '{print $1}')
new_configs=$(grep -E '^[A-Za-z0-9_].+=.+$' "${script_install_path}/${script_name}.conf.sample" | awk -F "=" '{print $1}')
for new_config in ${new_configs}; do
if [[ ${current_configs} =~ ${new_config} ]]; then
:
else
echo "Adding new config: ${new_config} into ${script_install_path}/${script_name}.conf"
echo "${new_config}=\"\"" >>"${script_install_path}/${script_name}.conf"
check_status "$?"
fi
done
# Remove blank lines, comments and sort config file
grep -E '^[A-Za-z0-9_].+=.+$' "${script_install_path}/${script_name}.conf" | sort >"${script_install_path}/${script_name}.conf_tmp"
mv "${script_install_path}/${script_name}.conf_tmp" "${script_install_path}/${script_name}.conf"
check_status "$?"
echo "[${caller}]: Running ${script_name} --version ..."
chmod +x "${script_install_path}/${script_name}"
${script_name} --version
check_status "$?"
}
check_update() {
local script_name script_path caller temp_file script_install_path
script_name="$(basename "${0}")"
script_path="$(dirname "$(readlink -f "$0")")"
caller="${script_name}->${FUNCNAME[0]}"
script_install_path="$(${script_name} script-path)"
echo "[${caller}]: Checking ${script_name} for update..."
temp_file="${TEMP_PATH}/${script_name}"
# The github raw hosting will not be updated immediately after I push the update to github. Need to wait about 5 minutes
if ! command -v curl &>/dev/null; then
[[ "$(get_distro id)" == +(debian|ubuntu|centos|almalinux|rhel) ]] && apt-get install -y curl
fi
curl -H 'Cache-Control: no-cache' -so "${temp_file}" "https://raw.githubusercontent.com/sofibox/${script_name}/main/${script_name}"
check_status "$?"
available_version="$(awk -F'=' '/^_APP_VERSION=/{ print $2 }' "${temp_file}" | sed 's/\"//g')"
this_version="${_APP_VERSION}"
echo ""
echo "Installed version is: v${this_version}"
echo "Online version is: v${available_version}"
echo ""
if [[ "ok" == "$(echo | awk "(${available_version} > ${this_version}) { print \"ok\"; }")" ]]; then
echo "[${caller}]: A new version of ${script_name} is available."
read -r -p "[${caller}->input]: Do you want to update ${script_name} to version ${available_version}? [default:Y] [Y/n]: " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
script_update "${script_install_path}"
else
echo "[${caller}->cancel]: Operation aborted!"
exit 0
fi
elif [[ "ok" == "$(echo | awk "(${available_version} == ${this_version}) { print \"ok\"; }")" ]]; then
echo "[${caller}]: You are using the latest version of ${script_name}."
else
echo "[${caller}]: You are using a newer version of ${script_name} than the latest available."
fi
rm -f "${temp_file}"
}
# This function is used to handle exit trap that can accept multiple trap arguments
# syntax: traps <traps_cleanup_function> SIG1 SIG2 SIG3 ... SIGN[N]
# eg: traps exit_function QUIT INT TERM EXIT
traps() {
local clean_function
clean_function="$1"
shift
for sig; do
trap "${clean_function} ${sig}" "${sig}"
done
}
# This function is used by traps() function to clean exit
exit_script() {
((CTRL_C_COUNT++))
if [[ ${CTRL_C_COUNT} == 1 ]]; then
local signal
signal="$1"
if [ "${signal}" == "INT" ]; then
echo "*** Warning, this script has been terminated by user: ${USER}!***"
fi
exit
fi
}
# This function is used to display the main help message from readme file.
# Usage: readme [file]
# Example: readme /docs/README.md
readme() {
local readme_file="${1}"
if [ -f "${readme_file}" ]; then
cat "${readme_file}"
echo ""
else
echo "Error, the readme file ${readme_file} does not exist."
exit 1
fi
}
# Print if verbose is enabled and not in scripting mode
info() {
local caller
caller="${SCRIPT_NAME}->${FUNCNAME[0]}"
if [[ ${VERBOSE} -ge "$1" ]]; then
echo "[${caller}]: $2"
fi
}
# Error handling that must exit the script manually
error() {
local caller
caller="${SCRIPT_NAME}->${FUNCNAME[0]}"
[[ "${SCRIPTING}" == false ]] && echo "[${caller}]: $1" >&2
[[ "${SCRIPTING}" == true ]] && echo "error"
exit 254
}
# Handling script simple status
check_status() {
local retval
retval="$1"
if [[ "${retval}" -eq 0 ]]; then
info 3 "[ OK ]"
else
error "[ FAILED ]"
fi
}
# Prompt user to reconfigure script
setup_wizard() {
local caller
caller="${SCRIPT_NAME}->${FUNCNAME[0]}"
echo -ne "[${caller}->input]: Do you want to run setup wizard? [y/n]: "
read -r answer
if [[ "${answer}" == "y" || "${answer}" == "Y" ]]; then
# Copy the sample config file from template
info 1 "Converting config file into unix format"
sed -i 's/\r$//' "${SCRIPT_PATH}/${SCRIPT_NAME}.conf.sample"
info 1 "Copying sample config file from template ..."
cp -f "${SCRIPT_PATH}/${SCRIPT_NAME}.conf.sample" "${CONFIG_FILE}"
check_status "$?"
info 1 "Using config file: ${CONFIG_FILE}"
read_config
else
info 1 "Ok, setup wizard skipped"
fi
exit 0
}
# Read config file
read_config() {
local caller
caller="${SCRIPT_NAME}->${FUNCNAME[0]}"
if [[ -z "${CONFIG_FILE}" ]]; then
info 2 "Using default config file: ${SCRIPT_PATH}/${SCRIPT_NAME}.conf"
CONFIG_FILE="${SCRIPT_PATH}/${SCRIPT_NAME}.conf"
else
info 2 "Using config file: ${CONFIG_FILE}"
fi
info 2 "Checking config file ${CONFIG_FILE} ..."
if [[ -f ${CONFIG_FILE} ]]; then
sed -i 's/\r$//' "${CONFIG_FILE}"
TEST_SOURCE="$(source "${CONFIG_FILE}" 2>&1 >/dev/null)"
RETVAL=$?
if [[ ${RETVAL} -eq 0 ]]; then
source "${CONFIG_FILE}" 2>/dev/null
else
info 1 "Warning, config file ${CONFIG_FILE} contains invalid syntax"
info 2 "Invalid syntax details:"
info 2 "${TEST_SOURCE}"
mv "${CONFIG_FILE}" "${CONFIG_FILE}".old
info 2 "The old configuration file has been backed up as ${CONFIG_FILE}.old"
setup_wizard
fi
else
echo "[${caller}]: Warning, config file ${CONFIG_FILE} not found"
setup_wizard
fi
}
# Resolve a given target to an IP and convert it to IP with slash notation
resolve() {
local target ip_lookup
target="$1"
# Convert target to IP with slash notation
if ipcalc -c -4 "${target}" >/dev/null 2>&1; then
ip_lookup="${target}"
elif ipcalc -c -6 "${target}" >/dev/null 2>&1; then
ip_lookup=$(ipcalc -6 "${target}" | awk '/^Full Address:/ { print $NF }')
else
local get_ip_lookup
get_ip_lookup=$(getent hosts "${target}" | awk '{ print $1 ; exit }')
if ipcalc -c -4 "${get_ip_lookup}" >/dev/null 2>&1; then
ip_lookup="${get_ip_lookup}"
elif ipcalc -c -6 "${get_ip_lookup}" >/dev/null 2>&1; then
ip_lookup=$(ipcalc -6 "${get_ip_lookup}" | awk '/^Full Address:/ { print $NF }')
else
ip_lookup=""
fi
fi
echo "${ip_lookup}"
}
# This is for checking or scanning IP / domain
scan() {
local caller ip_lookup ipv4_lookup ipv4_lookup_slash ipv6_full_lookup ipv6_full_lookup_slash ipv6_semi_compress_lookup ipv6_semi_compress_lookup_slash ipv6_compress_lookup ipv6_compress_lookup_slash log_paths found_count
caller="${SCRIPT_NAME}->${FUNCNAME[0]}"
found_count=0
log_paths=$(jq -r '.log_list[].path' "${LOG_LIST}")
if [[ -z "${TARGET}" ]]; then
error "No target specified for scanning (eg ${SCRIPT_NAME} -t example.com)"
fi
ip_lookup=$(resolve "${TARGET}")
if [ -z "${ip_lookup}" ]; then
error "No DNS record found for ${TARGET}"
fi
if ipcalc -c -4 "${ip_lookup}" >/dev/null 2>&1; then
ipv4_lookup="${ip_lookup}"
ipv4_lookup_slash="${ipv4_lookup//./\\.}"
info 0 "Checking target ${TARGET} resolved to ${ipv4_lookup}"
elif ipcalc -c -6 "${ip_lookup}" >/dev/null 2>&1; then
ipv6_full_lookup="${ip_lookup}"
ipv6_full_lookup_slash="${ipv6_full_lookup//:/\\:}"
ipv6_semi_compress_lookup=$(echo "${ipv6_full_lookup}" | awk -F: 'BEGIN {OFS=":"} {for (i=1; i<=NF; i++) if ($i == "0000") {$i="0"}; gsub(/0{1,3}:/,"::"); print}')
ipv6_semi_compress_lookup_slash="${ipv6_semi_compress_lookup//:/\\:}"
ipv6_compress_lookup=$(ipcalc -6 "${ipv6_full_lookup}" | awk '/^Address:/ { print $NF }')
ipv6_compress_lookup_slash="${ipv6_compress_lookup//:/\\:}"
info 0 "Checking target ${TARGET} resolved to ${ipv6_full_lookup}, ${ipv6_semi_compress_lookup} and ${ipv6_compress_lookup}"
else
error "No valid IP found for ${TARGET}"
fi
# loop log_paths
for log_path in ${log_paths}; do
log_status=$(jq -rc --arg path "${log_path}" '.log_list[] | select(.path==$path) | .status' "${LOG_LIST}")
if [[ ! -s "${log_path}" ]]; then
info 1 "Warning, log file ${log_path} not found or empty"
continue
fi
if [[ "${log_status}" == "disabled" ]]; then
info 1 "Warning, log file ${log_path} is disabled"
continue
fi
if ipcalc -c -4 "${ip_lookup}" >/dev/null 2>&1; then
search_status=$(grep -E "(^|[^.0-9a-fA-F])${ipv4_lookup_slash}([^.0-9a-fA^C]|$)|^${ipv4_lookup_slash}$" "${log_path}")
elif ipcalc -c -6 "${ip_lookup}" >/dev/null 2>&1; then
search_status=$(grep -E "(^|[^.0-9a-fA-F])${ipv6_full_lookup_slash}([^.0-9a-fA^C]|$)|^${ipv6_full_lookup_slash}$|(^|[^.0-9a-fA-F])${ipv6_semi_compress_lookup_slash}([^.0-9a-fA^C]|$)|^${ipv6_semi_compress_lookup_slash}$|(^|[^.0-9a-fA-F])${ipv6_compress_lookup_slash}([^.0-9a-fA^C]|$)|^${ipv6_compress_lookup_slash}$" "${log_path}")
else
search_status=""
fi
if [[ -n "${search_status}" ]]; then
((found_count++))
echo ""
log_description=$(jq -rc --arg path "${log_path}" '.log_list[] | select(.path==$path) | .description' "${LOG_LIST}")
echo "${found_count}) ${log_path} ${log_description}"
info 0 "Found ${TARGET} (${ip_lookup}) in ${log_path}"
echo "---------"
echo "Latest 12 records:"
echo "---"
echo "${search_status}" | tail -n 12
echo "---------"
echo ""
fi
done
if [[ ${found_count} -eq 0 ]]; then
info 0 "No records found for target ${TARGET} (${ip_lookup})"
fi
echo "Done"
}
############################
# MAIN FUNCTION START HERE #
############################
# This script was tested on Debian 11 (Bullseye)
declare SCRIPT_PATH SCRIPT_NAME SHORT_OPT_SPECS INDEX ACTION ARGNUM RETVAL
declare -A LONG_OPT_SPECS
SCRIPT_PATH="$(dirname "$(readlink -f "$0")")"
SCRIPT_NAME=$(basename -- "$0")
OS_TEMP_PATH="/tmp"
mkdir -p "${OS_TEMP_PATH}"
SHORT_OPT_SPECS=":hvsc:t:-:"
LONG_OPT_SPECS=(["ip"]=1 ["ip-address"]=1 ["domain"]=1 ["domain-name"]=1 ["config"]=1 ["target"]=1)
INDEX=$(($# + 1))
ACTION="$1"
ARGNUM="$#"
RETVAL=0
# This variable will be used as global
VERBOSE=false
SCRIPTING=false
# Make sure jq and ipcalc are installed
if ! command -v maxibuild &>/dev/null; then
error "Error, maxibuild is not installed, please install it first"
fi
maxibuild --include "jq ipcalc"
traps exit_script QUIT INT TERM EXIT
if [ ${ARGNUM} -eq 0 ]; then
error "No argument supplied. Please use '${SCRIPT_NAME} --help' for help"
fi
shift
while getopts "${SHORT_OPT_SPECS}" OPTION; do
while true; do
case "${OPTION}" in
-)
if [[ ${OPTARG[0]} =~ .*=.* ]]; then
OPTION=${OPTARG/=*/}
((${#OPTION} <= 1)) && {
echo "[${SCRIPT_NAME}]: Error, invalid long option '${OPTION}'" >&2
exit 1
}
if ((LONG_OPT_SPECS[\$OPTION] != 1)); then
echo "[${SCRIPT_NAME}]: Error, the option '${OPTION}' does not support this syntax"
exit 2
fi
OPTARG[0]=${OPTARG#*=}
else
OPTION="${OPTARG[0]}"
((${#OPTION} <= 1)) && {
echo "[${SCRIPT_NAME}]: Error, Invalid long option '${OPTION}'"
exit 1
}
OPTARG=("${@:OPTIND:LONG_OPT_SPECS[\$OPTION]}")
((OPTIND += LONG_OPT_SPECS[\$OPTION]))
((OPTIND > INDEX)) && {
echo "[${SCRIPT_NAME}]: Error, missing required arguments for option '${OPTION}'"
exit 2
}
fi
continue
;;
h | help)
ACTION="help"
;;
v | verbose)
VERBOSE=$((VERBOSE + 1))
;;
s | scripting)
SCRIPTING=true
VERBOSE=-1
;;
c | config)
CONFIG_FILE="${OPTARG[0]}"
;;
t | ip | ip-address | domain | domain-name | target)
TARGET="${OPTARG[0]}"
;;
?)
echo "[${SCRIPT_NAME}]: Syntax error: Unknown short option '${OPTARG[0]}'"
exit 2
;;
*)
echo "[${SCRIPT_NAME}]: Syntax error: Unknown long option '${OPTION}'"
exit 2
;;
esac
break
done
done
read_config
LOG_LIST="${SCRIPT_PATH}/log_list.json"
if [[ ! -f "${LOG_LIST}" ]]; then
echo "Warning, log_list.json not found, copying from sample file ..."
cp "${SCRIPT_PATH}/log_list.json.sample" "${LOG_LIST}"
echo ""
else
# Convert to unix format
sed -i 's/\r$//' "${LOG_LIST}"
# Check if log_list.json is valid
if ! jq empty "${LOG_LIST}" &>/dev/null; then
error "Error, log_list.json is not valid, please check it"
fi
fi
if [[ "${ACTION^^}" == +(-H|--HELP|HELP) ]]; then
readme "${SCRIPT_PATH}/readme.txt"
RETVAL=$?
exit ${RETVAL}
elif [[ "${ACTION^^}" == +(-V|--VERSION|VERSION) ]]; then
echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~="
echo ""
echo "Info: ${_APP_INFO}"
echo ""
echo "Version: ${_APP_VERSION_STATUS}"
echo ""
echo "${_AUTHOR}"
echo ""
echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~="
RETVAL=$?
exit ${RETVAL}
elif [[ "${ACTION^^}" == +(-T|--TEST|TEST) ]]; then
exit 0
elif [[ "${ACTION^^}" == +(SCAN|CHECK) ]]; then
scan "$@"
RETVAL=$?
exit ${RETVAL}
elif [[ "${ACTION^^}" == +(REPORT) ]]; then
report "$@"
RETVAL=$?
exit ${RETVAL}
elif [[ "${ACTION^^}" == +(-U|--UPDATE|UPDATE) ]]; then
check_update "$@"
RETVAL=$?
exit ${RETVAL}
elif [[ "${ACTION^^}" == +(GET-SCRIPT_PATH|SCRIPT-PATH) ]]; then
echo "${SCRIPT_PATH}"
exit "$?"
elif [[ "${ACTION^^}" == +(SHOW-OUTPUT|OUTPUT) ]]; then
if command -v "nano" &>/dev/null; then
nano -c "${OUTPUT_FILE}"
exit "$?"
else
vi "${OUTPUT_FILE}"
exit "$?"
fi
else
error "Unknown action '${ACTION}'. Please provide a valid action (eg: ${SCRIPT_NAME} +<action> *<option>)"
fi