#!/usr/bin/env bash

set -xeuo pipefail

SCRIPT_NAME=$(basename "$0")

function usage()
{
    cat <<EOF
${SCRIPT_NAME}: Launch and validate a shadow simulation to test arti

Usage:
  ${SCRIPT_NAME} : Launch and  validate a shadow simulation

Options:
  -h: Print this message.
  -s <seed>: Integer PRNG seed
EOF
}

SEED=1
while getopts "hs:" opt ; do
    case "$opt" in
	h) usage
	   exit 0
	   ;;
	s) SEED="$OPTARG"
	   ;;
	*) echo "Unknown option. (Run $0 -h for usage)"
	   exit 1
	   ;;
    esac
done

# Remove output of previous run
rm -rf shadow.data

export RUST_BACKTRACE=1

# Fix permissions on hidden service dir to prevent tor from bailing.
# TODO: isn't there a way to set the permissions in the git repo? Tried `git
# update-index --chmod`, but it refuses to set permissions on a directory.
chmod 700 shadow.data.template/hosts/fileserver-onion/hs
chmod 700 shadow.data.template/hosts/fileserver-onion-auth/hs
chmod 700 shadow.data.template/hosts/fileserver-onion-arti-auth/authorized_clients
chmod 700 shadow.data.template/hosts/fileserver-onion-arti-auth-ctor/ctor-store
chmod 700 shadow.data.template/hosts/articlient-onion-artiserver-auth-ctor/ctor-store

# Run the simulation
shadow \
  --log-level=debug \
  --template-directory=./shadow.data.template \
  --progress=true \
  --use-memory-manager=false \
  --use-worker-spinning=false \
  --seed="$SEED" \
  shadow.yaml \
  > shadow.log

# Check whether file transfers via arti inside the simulation succeeded
for HOST in articlient articlient-extra articlient-bridge articlient-bridge-obfs4; do
  successes="$(grep -c stream-success shadow.data/hosts/$HOST/tgen.*.stdout || true)"
  if [ "$successes" = 10 ]
  then
    echo "Simulation successful"
  else
    echo "Failed. Only got $successes successful streams."
    exit 1
  fi
done

# Look for any non-empty stderr files. In particular rust panics sometimes end
# up showing up here (even though we try to log them properly).
nonempty_stderr=$(find shadow.data/hosts/ -name '*.stderr' -size +0 -print -quit)
if [ -n "$nonempty_stderr" ]; then
  echo "Found non-empty stderr file $nonempty_stderr:"
  cat "$nonempty_stderr"
  exit 1
fi

# Look for errors in arti logs.
# TODO: Maybe fail on warnings, too? There currently are a few, though.
arti_errs=$(find shadow.data/hosts/ -name 'arti.log.txt' -exec grep --with-filename '\bERROR\b' \{\} \;)
if [ -n "$arti_errs" ]; then
  echo "Found arti errors:"
  echo "$arti_errs"
  exit 1
fi

HOSTS=(
  articlient-onion
  articlient-onion-auth
  articlient-onion-artiserver
  articlient-onion-artiserver-full-vanguards
  articlient-onion-artiserver-auth
  articlient-onion-artiserver-auth-2
  articlient-onion-artiserver-auth-ctor
  torclient-onion-artiserver
  torclient-onion-artiserver-full-vanguards
)

for HOST in "${HOSTS[@]}"; do
  successes="$(grep -c stream-success shadow.data/hosts/"$HOST"/tgen.*.stdout || true)"

  # NOTE: For the HS client tests we only require half of the streams to succeed
  # to work around the issue described in:
  # * https://github.com/shadow/shadow/issues/2544
  # * https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1399#note_2921505
  # * https://gitlab.torproject.org/tpo/core/arti/-/issues/1986
  if [ "$successes" -ge 1 ]
  then
    echo "Simulation successful"
  else
    echo "Failed. Only got $successes successful streams."
    exit 1
  fi
done

HOSTS=(
  articlient-onion
  articlient-onion-auth
  articlient-onion-artiserver
  articlient-onion-artiserver-full-vanguards
  articlient-onion-auth
  articlient-onion-artiserver-auth-ctor
  fileserver-onion-arti
  fileserver-onion-arti-full-vanguards
  fileserver-onion-arti-auth
  fileserver-onion-arti-auth-ctor
)

for HOST in "${HOSTS[@]}"; do
  # There should be only one such file per host.
  file=(shadow.data/hosts/"$HOST"/arti.log.txt)
  # TODO: this is a temporary measure until we implement other ways of testing
  # that the circuits we've built have the desired properties.
  bugs="$(grep -c Bug "${file[*]}" || true)"

  if [ "$bugs" -eq 0 ]
  then
    echo "Simulation successful"
  else
    echo "Failed. Found $bugs internal errors in ${file[*]}."
    exit 1
  fi
done

pushd shadow.data/hosts/articlient-bridge/
for PCAP in *.pcap; do
	# verify all connection are either from/to the bridge, or local.
	LEAK=$(tshark -r "$PCAP" 'ip.src != 100.0.0.2 && ip.dst != 100.0.0.2 && ip.dst != 127.0.0.0/8')
	if [ "$LEAK" ]; then
		echo "Found tcp leaks in PCAP: $PCAP"
	        echo "$LEAK"
		exit 1
	fi
done

DNS_LEAK=$(grep -l shadow_hostname_to_addr_ipv4 arti.*.strace || true)
if [ "$DNS_LEAK" ]; then
	echo "Found DNS leaks in $DNS_LEAK"
	exit 1
fi
popd
