#!/bin/sh
# ***************************************************************************
# *                                                                         *
# *                                                                         *
# *   Copyright (C) 2008 by Robert Hogan                                    *
# *   robert@roberthogan.net                                                *
# *   Copyright (C) 2012 by Jacob Appelbaum <jacob@torproject.org>          *
# *   Copyright (C) 2013 by David Goulet <dgoulet@ev0ke.net>                *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU General Public License as published by  *
# *   the Free Software Foundation; either version 2 of the License, or     *
# *   (at your option) any later version.                                   *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU General Public License for more details.                          *
# *                                                                         *
# *   You should have received a copy of the GNU General Public License     *
# *   along with this program; if not, write to the                         *
# *   Free Software Foundation, Inc.,                                       *
# *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
# ***************************************************************************
# *                                                                         *
# *   This is a modified version of a source file from the Tor project.     *
# *   Original copyright information follows:                               *
# ***************************************************************************
# Wrapper script for use of the torsocks(8) transparent socksification library
#
# There are three forms of usage for this script:
#
# /opt/local/bin/torsocks program [program arguments...]
#
# This form sets the users DYLD_INSERT_LIBRARIES environment variable so that torsocks(8)
# will be loaded to socksify the application then executes the specified 
# program (with the provided arguments). The following simple example might 
# be used to ssh to www.foo.org via a torsocks.conf(5) configured socks server:
#
# /opt/local/bin/torsocks ssh www.foo.org
#
# The second form allows for torsocks(8) to be switched on and off for a
# session (that is, it adds and removes torsocks from the DYLD_INSERT_LIBRARIES environment
# variable). This form must be _sourced_ into the user's existing session
# (and will only work with bourne shell users):
#
# . /opt/local/bin/torsocks on
# telnet www.foo.org 
# . /opt/local/bin/torsocks off
# 
# Or
# 
# source /opt/local/bin/torsocks on
# telnet www.foo.org
# source /opt/local/bin/torsocks off
#
# This script is originally from the debian torsocks package by
# Tamas Szerb <toma@rulez.org>
# Modified by Robert Hogan <robert@roberthogan.net> April 16th 2006
# Modified by David Goulet <dgoulet@ev0ke.net> 2013
# Modified by Alex Xu (Hello71) <alex_y_xu@yahoo.ca> 2018

prefix=/opt/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
LIBDIR="${libdir}/torsocks"
LIB_NAME="libtorsocks"
SHLIB_EXT="dylib"
SHLIB="${LIBDIR}/${LIB_NAME}.${SHLIB_EXT}"

# https://github.com/mkropat/sh-realpath
#
# Copyright (c) 2014 Michael Kropat
#
# 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.

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

resolve_symlinks() {
    _resolve_symlinks "$1"
}

_resolve_symlinks() {
    _assert_no_path_cycles "$@" || return

    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_dir_context_if_necessary() {
    if [ "$1" = . ]; then
        printf '%s\n' "$2"
    else
        _prepend_path_if_relative "$1" "$2"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac
}

_assert_no_path_cycles() {
    local target path

    target=$1
    shift

    for path in "$@"; do
        if [ "$path" = "$target" ]; then
            return 1
        fi
    done
}

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P)
}

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

# Set DYLD_INSERT_LIBRARIES variable with torsocks library path.
set_ld_preload ()
{
	case "$DYLD_INSERT_LIBRARIES" in
		*"${SHLIB}"*) ;;
		'')
			export DYLD_INSERT_LIBRARIES="${SHLIB}"
			;;
		*)
			export DYLD_INSERT_LIBRARIES="${SHLIB}:$DYLD_INSERT_LIBRARIES"
			;;
	esac

	# OS X specific env variable
	case "$OSTYPE" in
		darwin*)
			export DYLD_FORCE_FLAT_NAMESPACE=1
			;;
	esac
}

# Report error due to Apple's System Integrity Protection.
macos_sip_error ()
{
	printf "ERROR: %s is located in a directory protected by Apple's System Integrity Protection.\n" "$1" >&2
	exit 1
}

# Check if SIP is enabled and if the user is about to violate the blacklist.
macos_sip_check ()
{
	local app_path="$1"

	case "$OSTYPE" in
		darwin*)
			# We need to figure out if Apple's System Integrity Protection is
			# enabled on the users' system.
			case "$(/usr/bin/csrutil status)" in
				*enabled*)
					# It seems like /usr/** (with an exception of /usr/local/**),
					# /System/**, /sbin/**, and /bin/** are currently protected
					# using SIP.
					case "$(realpath "$app_path")" in
						/usr/local/*)
							# Must be listed before the match on /usr/*
							;;
						/usr/*|/System/*|/sbin/*|/bin/*)
							macos_sip_error "$app_path"
							;;
					esac
			esac
			;;
	esac
}

# Spawn a torified shell.
tor_shell ()
{
	set_ld_preload
	echo "New torified shell coming right up..."
	${SHELL:-/bin/sh}
}

torify_app ()
{
	local app_path="$(command -v "$1")"
	local getcap="$(PATH="$PATH:/usr/sbin:/sbin" command -v getcap)"
	local caps=

	if [ -z "$1" ]; then
		echo "Please provide an application to torify." >&2
	elif [ -z "$app_path" ]; then
		echo "ERROR: $1 cannot be found." >&2
		exit 1
	fi

	# This must be before torifying because getcap uses cap_get_file(3)
	# via syscall(2) which breaks torsocks.
	if [ -n "$getcap" ]; then
		caps="$("$getcap" "$app_path" 2>/dev/null)"
	fi

	# Check if Apple's System Integrity Protection is enabled if the user is
	# running on macOS.
	macos_sip_check "$app_path"

	# NEVER remove that line or else nothing it torified.
	set_ld_preload

	if [ -u "$app_path" ]; then
		echo "ERROR: $1 is setuid. torsocks will not work on a setuid executable." >&2
		exit 1
	elif [ -g "$app_path" ]; then
		echo "ERROR: $1 is setgid. torsocks will not work on a setgid executable." >&2
		exit 1
	elif [ -n "$caps" ]; then
		printf "ERROR: %s gains the following elevated capabilities. torsocks will not work with privileged executables.\n%s" "$app_path" "$caps" >&2
		exit 1
	fi

	exec "$@"
}

usage ()
{
	echo "torsocks 2.4.0"
	echo ""
	echo "$0 [OPTIONS] [COMMAND [arg ...]]"
	echo ""
	echo "usage: $0 command args"
	echo ""
	echo "Options:"
	echo "  -h, --help         Show this help"
	echo "      --shell        Spawn a torified shell"
	echo "      --version      Show version"
	echo "  -d, --debug        Set debug mode."
	echo "  -u, --user NAME    Username for the SOCKS5 authentication"
	echo "  -p, --pass NAME    Password for the SOCKS5 authentication"
	echo "  -i, --isolate      Automatic tor isolation. Can't be used with -u/-p"
	echo "  -6, --ipv6         Enable creation of IPv6 sockets"
	echo "  -a, --address HOST Specify Tor address"
	echo "  -P, --port PORT    Specify Tor port"
	echo "  on, off            Set/Unset your shell to use Torsocks by default"
	echo "                     Make sure to source the call when using this option. (See Examples)"
	echo "  show, sh           Show the current value of the LD_PRELOAD"
	echo ""
	echo "Examples:"
	echo ""
	echo "Simple use of torsocks with SSH"
	echo "    $ torsocks ssh user@host.com -p 1234"
	echo ""
	echo "Set your current shell in Tor mode."
	echo "    $ . torsocks on"
	echo ""
	echo "Please see torsocks(1), torsocks.conf(5) and torsocks(8) for more information."
}

# Check if we are being sourced.
check_script_sourced()
{
	if [ "$_" = "$0" ]; then
		echo "Torsocks MUST be sourced for this command to work" >&2
		echo "  $ . torsocks $1" >&2
		exit 1
	fi
}

if [ $# -eq 0 ] ; then
	usage
	exit 1
fi

if [ ! -e "$SHLIB" ]; then
   echo "ERROR: $SHLIB does not exist! Try re-installing torsocks." >&2
   exit 1
fi

while true;
do
	case "$1" in
		on)
			check_script_sourced "$1"
			set_ld_preload
			echo "Tor mode activated. Every command will be torified for this shell."
			break
			;;
		off)
			check_script_sourced "$1"
			export DYLD_INSERT_LIBRARIES="$(printf '%s' "$DYLD_INSERT_LIBRARIES" | sed "s#$SHLIB *##")"
			if [ -z "$DYLD_INSERT_LIBRARIES" ]; then
				unset DYLD_INSERT_LIBRARIES
				case "$OSTYPE" in
					darwin*)
						unset DYLD_FORCE_FLAT_NAMESPACE
						;;
				esac
			fi
			echo "Tor mode deactivated. Command will NOT go through Tor anymore."
			break
			;;
		show|sh)
			printf '%s="%s"\n' "DYLD_INSERT_LIBRARIES" "$DYLD_INSERT_LIBRARIES"
			break
			;;
		-h|--help)
			usage
			break
			;;
		-u|--user)
			if [ -z "$2" ]; then
				echo "Missing username to -u" >&2
				exit 1
			fi
			export TORSOCKS_USERNAME="$2"
			shift
			;;
		-p|--pass)
			if [ -z "$2" ]; then
				echo "Missing password to -p" >&2
				exit 1
			fi
			export TORSOCKS_PASSWORD="$2"
			shift
			;;
		-a|--address)
			if [ -z "$2" ]; then
				echo "Missing address to -a" >&2
				exit 1
			fi
			export TORSOCKS_TOR_ADDRESS="$2"
			shift
			;;
		-P|--port)
			if [ -z "$2" ]; then
				echo "Missing port to -P" >&2
				exit 1
			fi
			export TORSOCKS_TOR_PORT="$2"
			shift
			;;
		-i|--isolate)
			export TORSOCKS_ISOLATE_PID=1
			;;
		-6|--ipv6)
			export TORSOCKS_ENABLE_IPV6=1
			;;
		-d|--debug)
			# Set full DEBUG with 5 being the highest possible level.
			export TORSOCKS_LOG_LEVEL=5
			;;
		-q|--quiet)
			# Silence logging.
			export TORSOCKS_LOG_LEVEL=1
			;;
		--shell)
			tor_shell
			break
			;;
		--version)
			echo "Torsocks 2.4.0"
			break
			;;
		*)
			torify_app "$@"
			break
			;;
	esac
	shift
done
