#!/bin/sh

prog="ss"

usage()
{
	cat >&2 <<EOF
Usage: $prog { -t | -x } [options] [ FILTER ]

A fake ss stub that prints items depending on the variables
FAKE_NETSTAT_TCP_ESTABLISHED, FAKE_TCP_LISTEN,
FAKE_NETSTAT_UNIX_LISTEN, depending on command-line options.

Note that -n is ignored.

EOF
	exit 1
}

not_supported()
{
	echo "Options not supported in stub: $*" >&2
	usage
}

############################################################

#
parse_filter()
{
	# Very limited implementation:
	# We only expect to find || inside parentheses
	# We don't expect to see && - it is implied by juxtaposition
	# Operator for port comparison is ignored and assumed to be ==

	# Build lists of source ports and source IP addresses where
	# each entry is surrounded by '|' characters.  These lists can
	# be easily "searched" using the POSIX prefix and suffix
	# removal operators.
	in_parens=false
	sports="|"
	srcs="|"

	while [ -n "$1" ]; do
		case "$1" in
		\()
			in_parens=true
			shift
			;;
		\))
			in_parens=false
			shift
			;;
		\|\|)
			if ! $in_parens; then
				not_supported "|| in parentheses"
			fi
			shift
			;;
		sport)
			p="${3#:}"
			sports="${sports}${p}|"
			shift 3
			;;
		src)
			ip="${2#\[}"
			ip="${ip%\]}"
			srcs="${srcs}${ip}|"
			shift 2
			;;
		*)
			usage
			;;
		esac
	done
}

# Check if socket has matches in both ok_ips and ok_ports
filter_socket()
{
	ok_ips="$1"
	ok_ports="$2"
	socket="$3"

	ip="${socket%:*}"
	ip="${ip#\[}"
	ip="${ip%\]}"
	port="${socket##*:}"

	if [ "$ok_ports" != "|" ] &&
		[ "${ok_ports#*|"${port}"|}" = "$ok_ports" ]; then
		return 1
	fi
	if [ "$ok_ips" != "|" ] && [ "${ok_ips#*|"${ip}"|}" = "$ok_ips" ]; then
		return 1
	fi

	return 0
}

ss_tcp_established()
{
	if $header; then
		echo "Recv-Q Send-Q Local Address:Port Peer Address:Port"
	fi

	# Yes, lose the quoting so we can do a hacky parsing job
	# shellcheck disable=SC2048,SC2086
	parse_filter $*

	if ! "$kill"; then
		for i in $FAKE_NETSTAT_TCP_ESTABLISHED; do
			src="${i%|*}"
			dst="${i#*|}"
			if filter_socket "$srcs" "$sports" "$src"; then
				echo 0 0 "$src" "$dst"
			fi
		done
	fi

	if [ -z "$FAKE_NETSTAT_TCP_ESTABLISHED_FILE" ]; then
		return
	fi
	new="${FAKE_NETSTAT_TCP_ESTABLISHED_FILE}.new"
	: >"$new"
	while read -r src dst; do
		if filter_socket "$srcs" "$sports" "$src"; then
			echo 0 0 "$src" "$dst"
		else
			echo "${src} ${dst}" >>"$new"
		fi
	done <"$FAKE_NETSTAT_TCP_ESTABLISHED_FILE"
	if "$kill"; then
		mv "$new" "$FAKE_NETSTAT_TCP_ESTABLISHED_FILE"
	else
		rm "$new"
	fi
}

############################################################

unix_listen()
{
	if $header; then
		cat <<EOF
Netid State Recv-Q Send-Q   Local Address:Port   Peer Address:Port"
EOF
	fi

	# Yes, lose the quoting so we can do a hacky parsing job
	# shellcheck disable=SC2048,SC2086
	parse_filter $*

	_n=12345
	for _s in $FAKE_NETSTAT_UNIX_LISTEN; do
		# ss matches Unix domain sockets as either src or
		# sport.
		if filter_socket "$srcs" "$sports" "${_s}:" ||
			filter_socket "$srcs" "$sports" ":${_s}"; then
			printf "u_str LISTEN 0 128   %s %d   * 0\n" "$_s" "$_n"
			_n=$((_n + 1))
		fi
	done
}

############################################################

# Defaults.
tcp=false
unix=false
all=false
listen=false
header=true
kill=false

orig="$*"

while getopts "txnalHKh?" opt; do
	case "$opt" in
	t) tcp=true ;;
	x) unix=true ;;
	l) listen=true ;;
	a) all=true ;;
	H) header=false ;;
	K) kill=true ;;
	n) : ;;
	\? | h) usage ;;
	esac
done
shift $((OPTIND - 1))

$tcp || $unix || not_supported "$*"
if "$all"; then
	not_supported "$*"
fi

if $tcp; then
	if [ "$1" != "state" ] || [ "$2" != "established" ] || $listen; then
		usage
	fi

	shift 2

	# Yes, lose the quoting so we can do a hacky parsing job
	# shellcheck disable=SC2048,SC2086
	ss_tcp_established $*

	exit
fi

if $unix; then
	if ! $listen; then
		not_supported "$orig"
	fi

	# Yes, lose the quoting so we can do a hacky parsing job
	# shellcheck disable=SC2048,SC2086
	unix_listen $*

	exit
fi
