#!/bin/bash

prog="ss"

usage ()
{
	cat >&2 <<EOF
Usage: $prog { -t|--tcp | -x|--unix } [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%:*}"
    port="${socket##*:}"

    if [ "$ok_ports" != "|" -a "${ok_ports#*|${port}|}" = "$ok_ports" ] ; then
	return 1
    fi
    if [ "$ok_ips" != "|" -a "${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

    parse_filter $*

    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

    if [ -z "$FAKE_NETSTAT_TCP_ESTABLISHED_FILE" ] ; then
	    return
    fi
    while read src dst ; do
	if filter_socket "$srcs" "$sports" "$src" ; then
	    echo 0 0 "$src" "$dst"
	fi
    done <"$FAKE_NETSTAT_TCP_ESTABLISHED_FILE"
}

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

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

	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

orig="$*"
temp=$(getopt -n "$prog" -o "txnalHh" -l tcp -l unix -l help -- "$@")
[ $? -eq 0 ] || usage

eval set -- "$temp"

while true ; do
	case "$1" in
	--tcp|-t) tcp=true ; shift ;;
	--unix|-x) unix=true ; shift ;;
	-l) listen=true ; shift ;;
	-a) all=true ; shift ;;
	-H) header=false ; shift ;;
	-n) shift ;;
	--) shift ; break ;;
	-h|--help|*) usage ;;
	esac
done

$tcp || $unix || not_supported "$*"

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

	shift 2

	# Yes, lose the quoting so we can do a hacky parsing job
	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
	unix_listen $*

	exit
fi