#!/bin/sh /etc/rc.common
# shellcheck disable=SC2039  # "local" not defined in POSIX sh

START=99
STOP=10

USE_PROCD=1
PROG=/usr/bin/snort
MGR=/usr/bin/snort-mgr

validate_snort_section() {
	$MGR -q check || return 1
	uci_validate_section snort snort "${1}" \
		'enabled:bool:0' \
		'manual:bool:1' \
		'config_dir:string' \
		'interface:string'
}

download_rules () {
	# Prefer data storage if available, otherwise use /var/ns-snort.
	if [ -d "/mnt/data" ] ; then
		cache_dir="/mnt/data/ns-snort"
	else
		cache_dir="/var/ns-snort"
	fi
	mkdir -p "${cache_dir}"
	oinkcode="$(uci -q get snort.snort.oinkcode)"
	args=""
	if [ -n "$oinkcode" ]; then
		rm -f "${cache_dir}"/*community-rules.tar.gz
		rules="$(find "${cache_dir}" -type f -name "snortrules-*.tar.gz" 2>/dev/null)"
	else
		rm -f "${cache_dir}"/snortrules-*.tar.gz
		rules="$(find "${cache_dir}" -type f -name "*community-rules.tar.gz" 2>/dev/null)"
	fi
	if [ -z "$rules" ]; then
		# No rules found, try downloading if we have internet connectivity.
		if ping -c 1 -W 2 1.1.1.1 > /dev/null 2>&1 || ping -c 1 -W 2 8.8.8.8 > /dev/null 2>&1; then
			args="--download"
		else
			echo "Warning: snort rules not found and no internet connectivity, skipping download."
		fi
	fi
	# this is done every time the service is started
	attempt=0
	until /usr/bin/ns-snort-rules $args; do
		attempt=$((attempt + 1))
		if [ "$attempt" -ge 3 ]; then
			echo "Error: failed to process snort rules after 3 attempts."
			break
		fi
		sleep 5
	done
}

setup_tweaks() {
	tweaks_file="/var/ns-snort/ns_local.lua"
	echo -e "suppress = \n{\n" > $tweaks_file
	# suppress element format: gid,sid,direction,ip,description
	suppress=$(uci -q get snort.snort.ns_suppress)
	echo "$suppress" | grep -oE "'[^']*'|[^ ]+" | while IFS= read -r s; do
		s=$(echo $s | sed "s/'//g")
		gid=$(echo $s | cut -d, -f1)
		sid=$(echo $s | cut -d, -f2)
		direction=$(echo $s | cut -d, -f3)
		ip=$(echo $s | cut -d, -f4)
		echo -e "    { gid = $gid, sid = $sid, track = '$direction', ip = '$ip' },\n" >> $tweaks_file
	done
	echo "}" >> $tweaks_file
}

start_service() {
	# If you wish to use application-managed PID file:
	# output.logdir, in the snort lua config, determines the PID file location.
	# Add '--create-pidfile' to the 'command', below.

	local enabled
	local manual
	local config_dir
	local interface

	# nethesis patch: config_dir sometimes seems empty, read it using external command
	if [ "$(uci -q get snort.snort.config_dir)" == "/var/ns-snort" ]; then
		cdir="$(uci -q get snort.snort.config_dir)"
		mkdir -p "${cdir}/rules"
		find /etc/snort -type f ! -name snort.rules -exec cp '{}' "${cdir}" \;
		/usr/libexec/ns-snort-bypass-config
		download_rules
		setup_tweaks
	fi

	validate_snort_section snort || {
		echo "Validation failed, try 'snort-mgr check'."
		return 1
	}

	[ "$enabled" = 0 ] && return

	procd_open_instance
	if [ "$manual" = 0 ]; then
		local config_file=$($MGR setup)
		maxlen=$(uci -q get snort.nfq.queue_maxlen)
		if [ -z "${maxlen}" ]; then
			maxlen=1024
		fi
		procd_set_param command "$PROG" -c "${config_file}" --tweaks ns_local --daq nfq --daq-var queue_maxlen=${maxlen}
	else
		procd_set_param command $PROG -q -i "$interface" -c "${config_dir%/}/snort.lua" --tweaks local
		procd_set_param env SNORT_LUA_PATH="$config_dir"
		procd_set_param file $CONFIGFILE
	fi
	[ -x /sbin/ujail -a -e /etc/capabilities/snort.json ] && {
		chown -R snort:snort "$config_dir"
		procd_add_jail snort
		procd_set_param capabilities /etc/capabilities/snort.json
		procd_set_param user snort
		procd_set_param group snort
		procd_set_param no_new_privs 1
	}
	procd_set_param respawn
	procd_set_param stdout 1
	procd_set_param stderr 1
	procd_close_instance
}

stop_service()
{
	service_stop "$PROG"
	$MGR teardown
}

service_triggers()
{
	procd_add_reload_trigger "snort"
	procd_add_validation validate_snort_section
}

reload_service()
{
	stop
	start
}
