#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

#### meta start
#### project Whonix
#### category networking and apps
#### workstation_only yes
#### description
## Generates systemd unit files in
## <code>/usr/lib/systemd/system/anon-ws-disable-stacked-tor_autogen_*</code> which
## listen on common local ports used by popular Tor applications such as Tor
## Browser.
##
## Redirect Whonix-Workstation port <code>9050</code> to Whonix-Gateway port
## <code>9050</code> and so forth.
##
## Create a unix domain socket files such as
## /run/anon-ws-disable-stacked-tor/127.0.0.1_9050.sock and forward those
## to $GATEWAY_IP:9150 etc. See also:
## https://forums.whonix.org/t/tbbs-use-of-sockssocket-will-break-whonixs-tor-browser-implementation/19207
##
## system Tor default SocksSocket is /run/tor/socks
## redirect Whonix-Workstation unix domain socket file /run/tor/socks to Whonix-Gateway port 9050
##
## Debian /usr/share/tor/tor-service-defaults-torrc uses '/run/tor/control' Tor ControlSocket
## Redirect Whonix-Workstation unix domain socket file /run/tor/control to Whonix-Gateway port 9051
#### meta end

########
# init #
########

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose

# shellcheck source=../../../../../kicksecure/helper-scripts/usr/libexec/helper-scripts/strings.bsh
source /usr/libexec/helper-scripts/strings.bsh

MYDIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"

## Change to either system or package source code root folder.
cd -- "${MYDIR}"
cd -- ..
cd -- ..
cd -- ..

#####################
# built-in defaults #
#####################

## Redirect Whonix-Workstation port 9050 to Whonix-Gateway port 9050 and so forth.
file_port_tuples+=" port#9050:9050 " ## system Tor default SocksPort
file_port_tuples+=" port#9150:9150 " ## Tor Browser Bundle default SocksPort
file_port_tuples+=" port#9051:9051 " ## Tor default ControlPort
file_port_tuples+=" port#9151:9051 " ## Tor Browser Bundle default ControlPort
file_port_tuples+=" port#9102:9102 " ## Thunderbird

file_port_tuples+=" port6#9050:9050 " ## system Tor default SocksPort
file_port_tuples+=" port6#9150:9150 " ## Tor Browser Bundle default SocksPort
file_port_tuples+=" port6#9051:9051 " ## Tor default ControlPort
file_port_tuples+=" port6#9151:9051 " ## Tor Browser Bundle default ControlPort
file_port_tuples+=" port6#9102:9102 " ## Thunderbird

## Create a unix domain socket files such as
## /run/anon-ws-disable-stacked-tor/127.0.0.1_9050.sock and forward those
## to $GATEWAY_IP:9150 etc. See also:
## https://forums.whonix.org/t/tbbs-use-of-sockssocket-will-break-whonixs-tor-browser-implementation/19207
file_port_tuples+=" file#/run/anon-ws-disable-stacked-tor/127.0.0.1_9050.sock:9050 "
file_port_tuples+=" file#/run/anon-ws-disable-stacked-tor/127.0.0.1_9150.sock:9150 "
file_port_tuples+=" file#/run/anon-ws-disable-stacked-tor/127.0.0.1_9051.sock:9051 "
file_port_tuples+=" file#/run/anon-ws-disable-stacked-tor/127.0.0.1_9151.sock:9051 "

file_port_tuples+=" file6#/run/anon-ws-disable-stacked-tor/::1_9050.sock:9050 "
file_port_tuples+=" file6#/run/anon-ws-disable-stacked-tor/::1_9150.sock:9150 "
file_port_tuples+=" file6#/run/anon-ws-disable-stacked-tor/::1_9051.sock:9051 "
file_port_tuples+=" file6#/run/anon-ws-disable-stacked-tor/::1_9151.sock:9051 "

## TODO: IPv6 - How to make IPv6 equivalents of these?
##       Do we even need IPv6 equivalents?
##       If we do, do we need to keep IPv4 variants around?
##         Probably, yes, as long as there is no argument to use IPv6 by default.
##         Keeping IPv4 the default might be more stable code paths.
##
## system Tor default SocksSocket is /run/tor/socks
## redirect Whonix-Workstation unix domain socket file /run/tor/socks to Whonix-Gateway port 9050
file_port_tuples+=" file#/run/tor/socks:9050 "

## Debian /usr/share/tor/tor-service-defaults-torrc uses '/run/tor/control' Tor ControlSocket
## Redirect Whonix-Workstation unix domain socket file /run/tor/control to Whonix-Gateway port 9051
file_port_tuples+=" file#/run/tor/control:9051 "

########################
# parse config folders #
########################

## Process configuration folders. Allows overwriting GATEWAY_IP and GATEWAY_IP6.

shopt -s nullglob
for i in etc/anon-ws-disable-stacked-tor.d/*.conf rw/anon-ws-disable-stacked-tor.d/*.conf /usr/local/etc/anon-ws-disable-stacked-tor.d/*.conf; do
   bash_n_exit_code="0"
   bash_n_output="$(bash -n "${i}" 2>&1)" || { bash_n_exit_code="$?" ; true; };
   if [ ! "${bash_n_exit_code}" = "0" ]; then
      printf '%s\n' "Invalid config file: ${i}
bash_n_exit_code: ${bash_n_exit_code}
bash_n_output:
${bash_n_output}" >&2
      exit 1
   fi
   # shellcheck disable=SC1090
   source "${i}"
done

#############
# variables #
#############

default_if_empty GATEWAY_IP ""
default_if_empty GATEWAY_IP6 ""

if [ "${BASH_SOURCE[0]}" = "/usr/libexec/anon-ws-disable-stacked-tor/systemd-unit-files-generator" ]; then
   default_if_empty system_run "true"
   default_if_empty generated_comment "This file was auto generated by ${BASH_SOURCE[0]} at system runtime."

   if [ "${GATEWAY_IP}" = "" ] || [ "${GATEWAY_IP6}" = "" ]; then
      ## Provides variable $GATEWAY_IP and $GATEWAY_IP6 so it can be used by `source`d config snippets.
      eval "$(/usr/libexec/helper-scripts/settings_echo)"
   fi
else
   default_if_empty system_run "false"
   script_name_without_leading_dot="$(printf '%s\n' "${BASH_SOURCE[0]}" | str_replace "./" "/")"
   default_if_empty generated_comment "This file was auto generated by ${script_name_without_leading_dot} at package build time."
   if [ "${GATEWAY_IP}" = "" ]; then
      ## IP HARDCODED. Set GATEWAY_IP through a drop-in configuration snippet in /etc/anon-ws-disable-stacked-tor.d configuration folder instead.
      GATEWAY_IP="10.152.152.10"
   fi
   if [ "${GATEWAY_IP6}" = "" ]; then
      ## IP HARDCODED. Set GATEWAY_IP6 through a drop-in configuration snippet in /etc/anon-ws-disable-stacked-tor.d configuration folder instead.
      GATEWAY_IP6="fd19:c33d:88bc::10"
   fi
fi

#############
# generator #
#############

## Delete previously automatically generated systemd unit files by this script.
safe-rm -f -- usr/lib/systemd/system/anon-ws-disable-stacked-tor_autogen_*

for item in ${file_port_tuples} ; do
   type_item="${item%#*}"
   port_tuples="${item##*#}"

   source_port="${port_tuples%:*}"
   destination_port="${port_tuples##*:}"

   add_ipv6_condition='false'
   ipv6_condition_maybe=''

   if [ "${type_item}" = "port" ]; then
      listen_where="127.0.0.1:${source_port}"
      destination_where="${GATEWAY_IP}:${destination_port}"
      file_name="port_${source_port}"

   elif [ "${type_item}" = "port6" ]; then
      listen_where="[::1]:${source_port}"
      destination_where="${GATEWAY_IP6}:${destination_port}"
      file_name="port6_${source_port}"
      add_ipv6_condition='true'

   elif [ "${type_item}" = "file" ]; then
      listen_where="${source_port}"
      destination_where="${GATEWAY_IP}:${destination_port}"
      file_name=${source_port//\//\_}

   elif [ "${type_item}" = "file6" ]; then
      listen_where="${source_port}"
      destination_where="${GATEWAY_IP6}:${destination_port}"
      file_name=${source_port//\//\_}
      add_ipv6_condition='true'

   else
      printf '%s\n' "ERROR: Unrecognized type '${type_item}'!"
      continue
   fi

   if [ "${add_ipv6_condition}" = 'true' ]; then
     ipv6_condition_maybe='ConditionPathExists=/run/helper-scripts/ipv6-is-enabled'
   fi

   printf '%s\n' "\
## Copyright (C) 2018 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## ${generated_comment}

[Unit]
Description=redirect ${listen_where} to Whonix-Gateway port ${destination_port}
Documentation=https://www.whonix.org/wiki/Dev/anon-ws-disable-stacked-tor
ConditionPathExists=!/run/qubes/this-is-templatevm
After=detect-ipv6-enabled-in-kernel.service
${ipv6_condition_maybe}

[Socket]
ListenStream=${listen_where}
SocketUser=debian-tor
SocketMode=666
DirectoryMode=2755

[Install]
WantedBy=sockets.target" > "./usr/lib/systemd/system/anon-ws-disable-stacked-tor_autogen_${file_name}.socket"

   printf '%s\n' "\
## Copyright (C) 2018 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## ${generated_comment}

[Unit]
## IP HARDCODED. See next comment below.
Description=redirect ${listen_where} to Whonix-Gateway port ${destination_port}
Documentation=https://www.whonix.org/wiki/Dev/anon-ws-disable-stacked-tor
ConditionPathExists=!/run/qubes/this-is-templatevm
After=detect-ipv6-enabled-in-kernel.service
${ipv6_condition_maybe}
## IP HARDCODED. See next comment below.
After=anon-ws-disable-stacked-tor_autogen_${file_name}.socket
Requires=anon-ws-disable-stacked-tor_autogen_${file_name}.socket

[Service]
## IP HARDCODED. If you want to change IP, set variable GATEWAY_IP / GATEWAY_IP6
## through a drop-in configuration snippet in /etc/anon-ws-disable-stacked-tor.d
## configuration folder instead.
## Then run the systemd-socket-proxyd systemd-unit-files-generator.
#sudo /usr/libexec/anon-ws-disable-stacked-tor/systemd-unit-files-generator
## See Documentation:
## https://www.whonix.org/wiki/Redirect_Whonix-Workstation_Ports_or_Unix_Domain_Socket_Files_to_Whonix-Gateway
ExecStart=/usr/lib/systemd/systemd-socket-proxyd ${destination_where}
PrivateTmp=yes" > "./usr/lib/systemd/system/anon-ws-disable-stacked-tor_autogen_${file_name}.service"

   if [ "${system_run}" = "true" ]; then
      systemctl --system daemon-reload
      systemctl enable "anon-ws-disable-stacked-tor_autogen_${file_name}.socket"

      ## 'restart' rather than 'start' required to support running this script
      ## during runtime due to the following.
      ##
      ## Jun 06 05:38:49 host systemd[1]: anon-ws-disable-stacked-tor_autogen__var_run_tor_socks.socket: Socket unit configuration has changed while unit has been running, no open socket file descriptor left. The socket unit is not functional until restarted.
      systemctl restart "anon-ws-disable-stacked-tor_autogen_${file_name}.socket"
   fi

done
