#!/bin/bash

# Download a file via Tor.
#
# This script launches an instance of a Tor client and attempts to
# download a Release file from deb.debian.org.  It uses torsocks and
# curl to do the request and ca-certificates to authenticate the https
# endpoint.  It also uses netcat to talk to the tor instance it started
# to learn the local socks port and the PID of the detached tor daemon.
#
# Copyright (c) 2018 Peter Palfrader
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

set -e
set -u

if [ -n "${AUTOPKGTEST_TMP:-}" ]; then
  tmpdir=""

  # We are executing in Autopkgtest, check if peers can be reached at all
  # or if blocked networks will make the test fail later
  AUTH_DIRS_FILE="src/app/config/auth_dirs.inc"
  if [ ! -f "$AUTH_DIRS_FILE" ]; then
      echo "INFO: no $AUTH_DIRS_FILE - can't check if TOR is reachable to skip ($PWD)."
  fi

  TARGETS=$(grep -E -o '([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]+' "$AUTH_DIRS_FILE" | sort -u)
  if [ -z "$TARGETS" ]; then
      echo "SKIP: No targets extracted from $AUTH_DIRS_FILE - can't check if TOR is reachable to skip ($PWD)."
      exit 77
  fi

  TOR_REACHABLE=0
  for target in $TARGETS; do
      ip="${target%%:*}"
      port="${target##*:}"

      echo "Testing connection to Tor Directory Authority $ip:$port..."
      if nc -z -w 3 "$ip" "$port" 2>/dev/null; then
          echo "Successfully connected to $ip:$port"
          TOR_REACHABLE=1
          break
      fi
  done

  if [ "$TOR_REACHABLE" -eq 0 ]; then
      echo "SKIP: Unable to reach any default Tor Directory Authorities from $AUTH_DIRS_FILE."
      exit 77
  fi

  echo "Tor network is reachable. Proceeding with tests..."

  # Enter the designated temporary directory for the tests
  cd "$AUTOPKGTEST_TMP"
else
  tmpdir="$(mktemp -d /tmp/tortest.XXXXXX)"
  cd "$tmpdir"
fi

# A system default TOR service concurrent to the test might push the test system
# more likely to be rate limited - stop the default service
systemctl stop tor@default || /bin/true

LOGFILE="log"

torpid=""

cleanup() {
  if [ -n "$torpid" ]; then
    /sbin/start-stop-daemon --name tor --pid "$torpid" --stop --retry 35
    torpid=""
  fi
  if [ -f "$LOGFILE" ]; then
    cat "$LOGFILE"
  fi
  if [ -n "$tmpdir" ]; then
    rm -rf "$tmpdir"
  fi
}
trap "cleanup" EXIT

cat > torrc << EOF
RunAsDaemon 1
SafeLogging 0
SocksPort auto
DataDirectory $(pwd)/tor
Log notice file $LOGFILE
ControlSocket $(pwd)/ctl RelaxDirModeCheck
EOF

/usr/bin/tor -f torrc
echo "Tor started."

# Do not rely on this to be fast and working immediately or at least within the
# "--retry 5" of curl. Instead wait until this is truly fully initialized
# which take many minutes.
echo "Waiting for Tor to complete bootstrapping..."
until ( echo 'authenticate'; echo 'getinfo status/bootstrap-phase'; echo 'quit' ) | nc.openbsd -U ctl | tr -d '\r' | grep -q 'PROGRESS=100'; do
    echo "Tor not yet fully initialized ($(date))"
    sleep 30
done
echo "Tor is fully bootstrapped!"

torpid="$(
  ( echo 'authenticate';
    echo 'getinfo process/pid';
    echo 'quit' ) |
  nc.openbsd -U ctl |
  tr -d '\r' |
  awk -F= '$1 == "250-process/pid" { print $2 }'
  )"

sockslistener="$(
  ( echo 'authenticate';
    echo 'getinfo net/listeners/socks';
    echo 'quit' ) |
  nc.openbsd -U ctl |
  tr -d '\r' |
  awk -F= '$1 == "250-net/listeners/socks" { print $2 }' |
  tr -d '"'
  )"

IFS=: read socksaddr socksport <<< "$sockslistener"
if [ -z "$socksaddr" ] || [ -z "$socksport" ]; then
  echo >&2 "Could not figure out SOCKS address ($socksaddr) or port ($socksport)."
  exit 1
fi

echo "Getting file."


rm -f Release
torsocks 2>&1 -a "$socksaddr" -P "$socksport" \
  curl \
    --retry 5 \
    --max-time 300 \
    --location \
    -o Release \
    --stderr - \
    https://deb.debian.org/debian/dists/stable/Release && rc=0 || rc=$?

if [ "$rc" -eq 0 ] && grep 'Origin:[[:space:]]*Debian' Release; then
  echo "Successfully downloaded a Release file."
  exit 0
else
  echo >&2 "Downloading Release file failed (curl exit code $rc)."
  exit 1
fi
