Peter Alcock 8 months ago
parent
commit
c7e3f1d09c

BIN
.DS_Store


+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+# This is where the result of the script output.
+log/*
+!/log/.gitkeep
+
+# Config files.
+etc/*
+!/etc/.gitkeep

+ 28 - 0
.travis.yml

@@ -0,0 +1,28 @@
+branches:
+  only:
+    - master
+    - testing
+
+language: bash
+
+sudo: required
+dist: trusty
+
+before_install:
+  - sudo apt-add-repository "deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse"
+  - sudo apt-get -qq update
+  - sudo apt-get -t trusty-backports install shellcheck
+
+  ### --> https://github.com/travis-ci/travis-ci/issues/9484
+  # - curl -sSL "https://ftp-master.debian.org/keys/archive-key-7.0.asc" | sudo -E apt-key add -
+  # - echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" | sudo tee -a /etc/apt/sources.list > /dev/null
+  # - sudo apt-get update
+  # - sudo apt-get install shellcheck
+  ### -->
+
+# after_success:
+#   - test $TRAVIS_PULL_REQUEST = false && git push
+
+script:
+  - shellcheck --version
+  - shellcheck -s bash -e 1072,1094 -x src/* -x lib/* bin/multitor

+ 74 - 0
bin/multitor

@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+### BEG SCRIPT INFO
+#
+# Header:
+#
+#         fname : "multitor"
+#         cdate : "09.01.2018"
+#        author : "Michał Żurawski <trimstray@gmail.com>"
+#      tab_size : "2"
+#     soft_tabs : "yes"
+#
+# Description:
+#
+#   See README.md file for more information.
+#
+#   The multitor has been completely rewritten on the basis of:
+#     - Multi-TOR project written by Jan Seidl:
+#       https://github.com/jseidl/Multi-TOR
+#     - original source is (Sebastian Wain project):
+#       http://blog.databigbang.com/distributed-scraping-with-multiple-tor-circuits/
+#
+# License:
+#
+#   multitor, Copyright (C) 2018  Michał Żurawski
+#
+#   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 3 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, see <http://www.gnu.org/licenses/>.
+#
+### END SCRIPT INFO
+
+
+# The array that store call parameters.
+# shellcheck disable=SC2034
+__init_params=()
+__script_params=("$@")
+
+# Store the name of the script and directory call.
+readonly _init_name="$(basename "$0")"
+readonly _init_directory="$(dirname "$(readlink -f "$0")")"
+
+# Set root directory.
+readonly _rel="${_init_directory}/.."
+
+# Directory structure.
+# shellcheck disable=SC2154
+readonly _src="${_rel}/src"
+readonly _lib="${_rel}/lib"
+readonly _etc="${_rel}/etc"
+readonly _tml="${_rel}/templates"
+
+# shellcheck disable=SC1090,SC1091
+source "${_src}/settings"
+# shellcheck disable=SC1090
+source "${_src}/helpers"
+# shellcheck disable=SC1090
+source "${_src}/__init__"
+
+
+# We pass arguments to the __main__ function.
+# It is required if you want to run on arguments type $1, $2, ...
+__main__ "${__script_params[@]}"
+
+_exit_ "0"

+ 0 - 0
etc/.gitkeep


+ 33 - 0
lib/CheckConn

@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CheckConn()
+#
+# Description:
+#   Check connection to remote host.
+#
+# Usage:
+#   CheckConn host port
+#
+# Examples:
+#   CheckConn 172.20.20.50 22
+#
+
+function CheckConn() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="CheckConn"
+  local _STATE=0
+
+  local _port="$1"
+
+  # shellcheck disable=SC2154
+  timeout 1 bash -c "</dev/tcp/127.0.0.1/${_port}" >>"$_log_stdout" 2>&1
+
+  _STATE="$?"
+
+  return $_STATE
+
+}

+ 74 - 0
lib/CheckProxyProcess

@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CheckProxyProcess()
+#
+# Description:
+#   Manages proxy processes.
+#
+# Usage:
+#   CheckProxyProcess
+#
+# Examples:
+#   CheckProxyProcess
+#
+
+function CheckProxyProcess() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="CheckProxyProcess"
+  local _STATE=0
+
+  # shellcheck disable=SC2034
+  local proc_type="$1"
+
+  # First, we remove current processes.
+  #_proxy_processes=($(pidof "$proc_type"))
+  IFS=" " read -r -a _proxy_processes <<< "$(pidof "$proc_type")"
+
+  if [[ ${#_proxy_processes[@]} -ne 0 ]] ; then
+
+    for _tpr in "${_proxy_processes[@]}" ; do
+
+      # shellcheck disable=SC2154
+      for _rg in "${_proxy_list[@]}" ; do
+
+        _tmp_proxy_list+="\\|$_rg"
+
+      done
+
+      # In this case, we can search for a specific identifier (${_tpr}).
+      # shellcheck disable=SC2009,SC2034,SC2154
+      _hpr=$(ps -ax | grep "$proc_type" | grep -v "grep" | grep "$_tpr" | grep "multitor${_tmp_proxy_list}")
+
+      if [[ -z "$_hpr" ]] ; then
+
+        _logger "warn" \
+          "${_FUNCTION_ID}()" \
+          "not found or unknown proxy process for ${proc_type}"
+
+      else
+
+        _proxy_processes_list+=("$_tpr")
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added proxy process with '${proc_type}:${_tpr}' pid"
+
+      fi
+
+    done
+
+  else
+
+    _logger "warn" \
+      "${_FUNCTION_ID}()" \
+      "not found proxy processes"
+
+  fi
+
+  return $_STATE
+
+}

+ 86 - 0
lib/CreateProxyProcess

@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CreateProxyProcess()
+#
+# Description:
+#   It creates tor load balancer with haproxy.
+#
+# Usage:
+#   CreateProxyProcess
+#
+# Examples:
+#   CreateProxyProcess
+#
+
+function CreateProxyProcess() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="CreateProxyProcess"
+  local _STATE=0
+
+  # Port number from HAProxy config file.
+  local _bk_port_tmp="16379"
+  local _bk_port_new=0
+
+  # shellcheck disable=SC2154
+  for _bk_port in "${_proxy_ports[@]}" ; do
+
+    if [[ "$proxy_state" -eq 1 ]] && \
+       [[ ! "$frontend_type" == "haproxy" ]] ; then
+
+      # shellcheck disable=SC2034
+      _bk_port_new="$_bk_port"
+
+      # shellcheck disable=SC2154
+      printf "  server            socks-process-%d 127.0.0.1:%d check fall 3 rise 2\\n" "$_bk_port" "$_bk_port_new" \
+      >>"${_tml_ha1}"
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "added backend port (haproxy): '$_bk_port'"
+
+    else
+
+      # shellcheck disable=SC2034
+      _bk_port_new=$((_bk_port_tmp - 1000))
+
+      # shellcheck disable=SC2154
+      printf "  server            %s-process-%d 127.0.0.1:%d check fall 3 rise 2\\n" "$proxy_type" "$_bk_port_new" "$_bk_port_new" \
+      >>"${_tml_ha1}"
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "added backend port (haproxy): '$_bk_port_new'"
+
+      CreateWebProxyProcess
+
+      _bk_port_tmp=$((_bk_port_tmp + 1))
+
+    fi
+
+  done
+
+  if [[ ! "$frontend_type" == "haproxy" ]] ; then
+
+    # Port number from HAProxy config file.
+    # shellcheck disable=SC2034
+    _bk_port="16379"
+    _bk_port_new=$((_bk_port - 1000))
+
+    CreateWebProxyProcess
+
+  fi
+
+  # shellcheck disable=SC2154
+  # Init HAProxy process.
+  haproxy -f "${_tml_ha1}" && \
+  _logger "info" \
+    "${_FUNCTION_ID}()" \
+    "init proxy process with '${_tml_ha1}' config file"
+
+  return $_STATE
+
+}

+ 62 - 0
lib/CreateTorDirectory

@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CreateTorDirectory()
+#
+# Description:
+#   It creates tor processes data directory.
+#
+# Usage:
+#   CreateTorDirectory
+#
+# Examples:
+#   CreateTorDirectory
+#
+
+function CreateTorDirectory() {
+
+  local _FUNCTION_ID="CreateTorDirectory"
+  local _STATE=0
+
+  # shellcheck disable=SC2154
+  mkdir -m 0700 "$_proc_dir" > /dev/null 2>&1 ; _kstate="$?"
+
+  if [[ $_kstate -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "created tor process directory: ${_proc_dir}"
+
+    # shellcheck disable=SC2154
+    chown "${_arg_uname}" "${_proc_dir}" >>"$_log_stdout" 2>&1
+    _kstate="$?"
+
+    if [[ $_kstate -eq 0 ]] ; then
+      
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "changed owner properly to: '${_arg_uname}'"
+
+    else
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "the owner could not be changed"
+
+    fi
+
+  else
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "not created tor process directory"
+
+  fi
+
+  unset _kstate
+
+  return $_STATE
+
+}

+ 117 - 0
lib/CreateTorProcess

@@ -0,0 +1,117 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CreateTorProcess()
+#
+# Description:
+#   It creates tor processes.
+#
+# Usage:
+#   CreateTorProcess "username" "num" "num"
+#
+# Examples:
+#   CreateTorProcess ${user_name} ${socks_port_number} ${control_port_number}
+#
+
+function CreateTorProcess() {
+
+  local _FUNCTION_ID="CreateTorProcess"
+  local _STATE=0
+
+  local _arg_uname="$1"
+  local _arg_socks="$2"
+  local _arg_control="$3"
+
+  # shellcheck disable=SC2154
+  local _proc_dir="${_multitor_directory}/${_arg_socks}"
+  local _torrc_config="${_proc_dir}/${_arg_socks}.torrc"
+
+  # shellcheck disable=SC2034
+  local _tport_state=0
+
+  # We create a directory for the new tor process.
+  CreateTorDirectory
+
+  # We save the hash of the password to the configuration file.
+  # shellcheck disable=SC2154
+  echo "HashedControlPassword ${_pass_hash}" > "${_torrc_config}"
+  _kstate="$?"
+
+  if [[ $_kstate -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "saved HashedControlPassword correctly"
+
+  else
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "not saved HashedControlPassword correctly"
+
+  fi
+
+  # shellcheck disable=SC2154
+  chmod 0400 "${_torrc_config}" >>"$_log_stdout" 2>&1 && \
+  chown "${_arg_uname}:${_arg_uname}" "${_torrc_config}" >>"$_log_stdout" 2>&1
+  _kstate="$?"
+
+  if [[ $_kstate -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "change permission and owner correctly"
+
+  else
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "not changed permission and owner correctly"
+
+  fi
+
+  # shellcheck disable=SC2024
+  sudo -u "$_arg_uname" tor -f "${_torrc_config}" \
+       --RunAsDaemon 1 \
+       --CookieAuthentication 0 \
+       --SocksPort "$_arg_socks" \
+       --ControlPort "$_arg_control" \
+       --PidFile "${_proc_dir}/${_arg_socks}.pid" \
+       --DataDirectory "${_proc_dir}" \
+       --SocksBindAddress 127.0.0.1 \
+       --NewCircuitPeriod 15 \
+       --MaxCircuitDirtiness 15 \
+       --NumEntryGuards 8 \
+       --CircuitBuildTimeout 5 \
+       --ExitRelay 0 \
+       --RefuseUnknownExits 0 \
+       --ClientOnly 1 \
+       --StrictNodes 1 \
+       --AllowSingleHopCircuits 1 \
+       >>"$_log_stdout" 2>&1 ; _kstate="$?"
+
+  if [[ $_kstate -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "create process: '${_arg_socks}.pid'"
+
+    _tor_processes_done=$((_tor_processes_done + 1))
+
+  else
+
+    _logger "warn" \
+      "${_FUNCTION_ID}()" \
+      "no process was created"
+
+    _tor_processes_fail=$((_tor_processes_fail + 1))
+
+  fi
+
+  unset _kstate
+
+  return $_STATE
+
+}

+ 183 - 0
lib/CreateWebProxyProcess

@@ -0,0 +1,183 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: CreateWebProxyProcess()
+#
+# Description:
+#   It creates web proxy process like a polipo, privoxy or hpts
+#
+# Usage:
+#   CreateWebProxyProcess
+#
+# Examples:
+#   CreateWebProxyProcess
+#
+
+function CreateWebProxyProcess() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="CreateWebProxyProcess"
+  local _STATE=0
+
+  local _proxy_http_state=0
+
+  # shellcheck disable=SC2154
+  if [[ "$frontend_type" == "haproxy" ]] ; then
+
+    # shellcheck disable=SC2154
+    if [[ "$proxy_type" = *"polipo"* ]] || \
+       [[ "$proxy_type" = *"privoxy"* ]] || \
+       [[ "$proxy_type" = *"hpts"* ]] ; then
+
+      if [[ "$proxy_type" == "polipo" ]] ; then
+
+        # shellcheck disable=SC2154
+        _pfd="${_tml_po1}.${_bk_port_new}"
+
+        # shellcheck disable=SC2154
+        cp "${_tml_po0}" "${_pfd}"
+
+        printf "proxyPort = %d\\n" "$_bk_port_new" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2154
+        printf "socksParentProxy = \"127.0.0.1:%d\"\\n" "$_bk_port" \
+        >>"${_pfd}"
+
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (polipo): '$_bk_port'"
+
+        polipo -c "${_pfd}"
+
+      elif [[ "$proxy_type" == "privoxy" ]] ; then
+
+        # shellcheck disable=SC2154
+        _pfd="${_tml_po1}.${_bk_port_new}"
+
+        # shellcheck disable=SC2154
+        cp "${_tml_po0}" "${_pfd}"
+
+        # https://github.com/trimstray/multitor/issues/15
+        # shellcheck disable=SC2154,SC2129
+        # printf "logfile privoxy.%d\\n" "$_bk_port_new" \
+        # >>"${_pfd}"
+
+        # shellcheck disable=SC2154,SC2129
+        printf "listen-address 127.0.0.1:%d\\n" "$_bk_port_new" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2154,SC2129
+        printf "forward-socks5t / 127.0.0.1:%d .\\n" "$_bk_port" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2034
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (privoxy): '$_bk_port'"
+
+        privoxy "${_pfd}"
+
+      elif [[ "$proxy_type" == "hpts" ]] ; then
+
+        # shellcheck disable=SC2034
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (hpts): '$_bk_port'"
+
+        hpts -s 127.0.0.1:"${_bk_port}" -p "${_bk_port_new}" >/dev/null 2>&1 &
+
+      fi
+
+    fi
+
+  else
+
+    # shellcheck disable=SC2154
+    if [[ "$proxy_type" = *"polipo"* ]] || \
+       [[ "$proxy_type" = *"privoxy"* ]] || \
+       [[ "$proxy_type" = *"hpts"* ]] ; then
+
+      if [[ "$proxy_type" == "polipo" ]] ; then
+
+        # shellcheck disable=SC2154
+        # _pfd="${_tml_po1}.${_bk_port_new}"
+        _pfd="${_tml_po1}"
+
+        # shellcheck disable=SC2154
+        cp "${_tml_po0}" "${_pfd}"
+
+        printf "proxyPort = %d\\n" "$_bk_port_new" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2154
+        printf "socksParentProxy = \"127.0.0.1:%d\"\\n" "$_bk_port" \
+        >>"${_pfd}"
+
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (polipo): '$_bk_port'"
+
+        polipo -c "${_pfd}"
+
+      elif [[ "$proxy_type" == "privoxy" ]] ; then
+
+        # shellcheck disable=SC2154
+        # _pfd="${_tml_po1}.${_bk_port_new}"
+        _pfd="${_tml_po1}"
+
+        # shellcheck disable=SC2154
+        cp "${_tml_po0}" "${_pfd}"
+
+        # https://github.com/trimstray/multitor/issues/15
+        # shellcheck disable=SC2154,SC2129
+        # printf "logfile privoxy.%d\\n" "$_bk_port_new" \
+        # >>"${_pfd}"
+
+        # shellcheck disable=SC2154,SC2129
+        printf "listen-address 127.0.0.1:%d\\n" "$_bk_port_new" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2154,SC2129
+        printf "forward-socks5t / 127.0.0.1:%d .\\n" "$_bk_port" \
+        >>"${_pfd}"
+
+        # shellcheck disable=SC2034
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (privoxy): '$_bk_port'"
+
+        privoxy "${_pfd}"
+
+      elif [[ "$proxy_type" == "hpts" ]] ; then
+
+        # shellcheck disable=SC2034
+        _proxy_http_state=1
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "added backend port (hpts): '$_bk_port'"
+
+        hpts -s 127.0.0.1:"${_bk_port}" -p "${_bk_port_new}" >/dev/null 2>&1 &
+
+      fi
+
+    fi
+
+  fi
+
+  return $_STATE
+
+}

+ 44 - 0
lib/DestroyProcess

@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: DestroyProcess()
+#
+# Description:
+#   It destroys currently running tor process.
+#
+# Usage:
+#   DestroyProcess "id"
+#
+# Examples:
+#   DestroyProcess ${_tpr}"
+#
+
+function DestroyProcess() {
+
+  local _FUNCTION_ID="DestroyProcess"
+  local _STATE=0
+
+  local _arg_tproc="$1"
+
+  # shellcheck disable=SC2154
+  kill -9 "$_arg_tproc" >>"$_log_stdout" 2>&1 ; _kstate="$?"
+
+  if [[ $_kstate -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "killed tor process: '${_arg_tproc}'"
+
+  else
+
+    _logger "warn" \
+      "${_FUNCTION_ID}()" \
+      "not killed tor process (maybe not found?)"
+
+  fi
+
+  return $_STATE
+
+}

+ 37 - 0
lib/GetTorProcess

@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: GetTorProcess()
+#
+# Description:
+#   Get info about tor process.
+#
+# Usage:
+#   GetTorProcess
+#
+# Examples:
+#   GetTorProcess
+#
+
+function GetTorProcess() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="GetTorProcess"
+  local _STATE=0
+
+  # shellcheck disable=SC2034,SC2154
+  _tor_proc_id=$(echo "$_tproc" | awk '{print $1}')
+  # shellcheck disable=SC2034
+  _tor_proc_config=$(echo "$_tproc" | awk '{print $7}')
+  # shellcheck disable=SC2034
+  _tor_proc_socks=$(echo "$_tproc" | awk '{print $13}')
+  # shellcheck disable=SC2034
+  _tor_proc_control=$(echo "$_tproc" | awk '{print $15}')
+  # shellcheck disable=SC2034
+  _tor_prod_data_dir=$(echo "$_tproc" | awk '{print $19}')
+
+  return $_STATE
+
+}

+ 60 - 0
lib/OutputGen

@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: OutputGen()
+#
+# Description:
+#   Show info about process on the screen.
+#
+# Usage:
+#   OutputGen
+#
+# Examples:
+#   OutputGen
+#
+
+function OutputGen() {
+
+  # shellcheck disable=SC2034
+  local _FUNCTION_ID="OutputGen"
+  local _STATE=0
+
+  # Check that _tor_proc_socks and _tor_proc_control are available.
+  # shellcheck disable=SC2154
+  CheckConn "$_tor_proc_socks" ; local _tport_state_socks="$?"
+  # shellcheck disable=SC2154
+  CheckConn "$_tor_proc_control" ; local _tport_state_control="$?"
+
+  if [[ $_tport_state_socks -ne 0 ]] ; then
+
+    export _tport_status_socks="false"
+
+  elif [[ $_tport_state_control -ne 0 ]] ; then
+
+    export _tport_status_control="false"
+
+  else
+
+    export _tport_status_socks="true"
+    export _tport_status_control="true"
+
+  fi
+
+  # shellcheck disable=SC2154
+  printf "     \\e[1;30mprocess_id\\e[m: \\e[1;39m%s\\e[m\\n" "$_tor_proc_id"
+  # shellcheck disable=SC2154
+  printf " \\e[1;30mprocess_status\\e[m: \\e[1;32m%s\\e[m\\n" "$_tor_pid_status"
+  printf "     \\e[1;30msocks_port\\e[m: \\e[0;33m%d\\e[m\\n" "$_tor_proc_socks"
+  # shellcheck disable=SC2154
+  printf "   \\e[1;30msocks_status\\e[m: \\e[0;32m%s\\e[m\\n" "$_tport_status_socks"
+  printf "   \\e[1;30mcontrol_port\\e[m: \\e[0;33m%d\\e[m\\n" "$_tor_proc_control"
+  # shellcheck disable=SC2154
+  printf " \\e[1;30mcontrol_status\\e[m: \\e[0;32m%s\\e[m\\n" "$_tport_status_control"
+  # shellcheck disable=SC2154
+  printf "       \\e[1;30mdata_dir\\e[m: \\e[0;37m%s\\e[m\\n" "$_tor_prod_data_dir"
+
+  return $_STATE
+
+}

+ 0 - 0
log/.gitkeep


+ 60 - 0
setup.sh

@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+readonly _dir="$(dirname "$(readlink -f "$0")")"
+
+# shellcheck disable=SC2034
+_arg="$1"
+
+if [[ "$1" == "install" ]] ; then
+
+  printf "%s\\n" "Create symbolic link to /usr/local/bin"
+
+  if [[ -e "${_dir}/bin/multitor" ]] ; then
+
+    if [[ ! -e "/usr/local/bin/multitor" ]] ; then
+
+      ln -s "${_dir}/bin/multitor" /usr/local/bin
+
+    fi
+
+  fi
+
+  printf "%s\\n" "Create man page to /usr/local/man/man8"
+
+  if [[ -e "${_dir}/static/man8/multitor.8" ]] ; then
+
+    if [[ ! -e "/usr/local/man/man8/multitor.8.gz" ]] ; then
+
+      mkdir -p /usr/local/man/man8
+      cp "${_dir}/static/man8/multitor.8" /usr/local/man/man8
+      gzip /usr/local/man/man8/multitor.8
+
+    fi
+
+  fi
+
+elif [[ "$1" == "uninstall" ]] ; then
+
+  printf "%s\\n" "Remove symbolic link from /usr/local/bin"
+
+  if [[ -L "/usr/local/bin/multitor" ]] ; then
+
+    unlink /usr/local/bin/multitor
+
+  fi
+
+  printf "%s\\n" "Remove man page from /usr/local/man/man8"
+
+  if [[ -e "/usr/local/man/man8/multitor.8.gz" ]] ; then
+
+    rm /usr/local/man/man8/multitor.8.gz
+
+  fi
+
+else
+
+  printf "Usage:\\n  ./setup.sh install     (Install)\\n  ./setup.sh uninstall   (Uninstall)\\n"
+
+fi
+
+exit 0

+ 1040 - 0
src/__init__

@@ -0,0 +1,1040 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+################################################################################
+######################### Main function (script init) ##########################
+################################################################################
+
+function __main__() {
+
+  local _FUNCTION_ID="__main__"
+  local _STATE="0"
+
+  # Stores the current date.
+  readonly _cdate=$(date +%Y%m%d)
+
+  # Variables related to the log file. Divided into three parts due
+  # to the better possibility of manipulation for the user.
+  # shellcheck disable=SC2154
+  readonly _log_directory="${_rel}/log"
+  # shellcheck disable=SC2154
+  readonly _log_file="${_init_name}.${_cdate}.log"
+  readonly _log_stdout="${_log_directory}/stdout.log"
+  readonly _log_path="${_log_directory}/${_log_file}"
+
+  # We check if we are a root user.
+  if [[ "$EUID" -ne 0 ]] ; then
+
+    printf "EUID is not equal 0 (no root user)\\n"
+    _exit_ "1"
+
+  fi
+
+  # Path to import file.
+  # shellcheck disable=SC2154
+  readonly _import_path="${_src}/import"
+
+  # External configuration file (-c|--config script param).
+  config=""
+  load_state="0"
+
+  # Declaration of output variables (--debug and --verbose params).
+  stdout_mode=""
+  verbose_mode="false"
+  # Enable/disable output colors.
+  # shellcheck disable=SC2034
+  s_color="true"
+
+  # Declaration of total execution time.
+  time_tracking="false"
+  export _cmdtime_state="0"
+
+  # Create multitor directory.
+  readonly _multitor_directory="/var/lib/multitor"
+
+  # We place here used commands at script runtime, as strings to anything
+  # unnecessarily run.
+  readonly commands=(basename dirname stat ps date grep cut sed awk chown \
+                     chmod mkdir pidof tor sudo nc kill \
+                     haproxy polipo privoxy hpts)
+
+  # If you intend to specify the full path to the command we do it like:
+  # readonly exec_gzip="/bin/gzip"
+
+  # Stores the names of the missing commands.
+  missing_hash=()
+  missing_counter="0"
+
+  for i in "${commands[@]}" ; do
+
+    if [[ ! -z "$i" ]] ; then
+
+      hash "$i" >/dev/null 2>&1 ; state="$?"
+
+      # If the command was not found put it in the array
+      if [[ "$state" -ne 0 ]] ; then
+
+        missing_hash+=("$i")
+        ((missing_counter++))
+
+      fi
+
+    fi
+
+  done
+
+  # It is a good idea to terminate the script at this stage
+  # with information for the user to fix the errors if at least one
+  # of the required commands in the commands array is not found.
+  if [[ "$missing_counter" -gt 0 ]] ; then
+
+    printf "not found in PATH: %s\\n" "${missing_hash[*]}" >&2
+    _exit_ "1"
+
+  fi
+
+  if [[ "$time_tracking" == "true" ]] ; then
+    _begtime=$(date +%s) ; fi
+
+  # shellcheck disable=SC2154
+  _logger "init" \
+    "init '${_init_name}' in '${_init_directory}'" && \
+  _logger "info" \
+    "__init_params[] = (${__init_params[*]})," \
+    "__script_params[] = (${__script_params[*]})"
+
+  # Include import file.
+  _load "null" "$_import_path"
+
+  # Specifies the call parameters of the script, the exact description
+  # can be found in _help_ and file README.md.
+  local _short_opt="i:ksnu:"
+  local _long_opt="help,debug,verbose,init:,kill,show-id,new-id,user:,socks-port:,control-port:,proxy:,haproxy"
+
+  _GETOPT_PARAMS=$(getopt -o "${_short_opt}" --long "${_long_opt}" \
+                   -n "${_init_name}" -- "${__script_params[@]}")
+
+  # With this structure, in the case of problems with the parameters placed
+  # in the _GETOPT_PARAMS variable we finish the script. Keep this in mind
+  # because it has some consequences - the __main __() function will not be
+  # executed.
+
+  # Ends an error if the parameter or its argument is not valid.
+  _getopt_state="$?"
+  if [ "$_getopt_state" != 0 ] ; then
+    _exit_ "1"
+  # Ends if no parameter is specified.
+  elif [[ "${#__script_params[@]}" -eq 0 ]] ; then
+    _exit_ "0"
+  fi
+
+  eval set -- "$_GETOPT_PARAMS"
+  while true ; do
+
+    case $1 in
+
+      --help)
+
+        _help_
+
+        shift ; _exit_ "0" ;;
+
+      --debug)
+
+        export stdout_mode="debug"
+
+        shift ;;
+
+      --verbose)
+
+        export verbose_mode="true"
+
+        shift ;;
+
+      -i|--init)
+
+        export init_state="1"
+
+        export init_number="${2}"
+
+        shift 2 ;;
+
+      -k|--kill)
+
+        export kill_state="1"
+
+        export kill_status="0"
+
+        shift ;;
+
+      -s|--show-id)
+
+        export show_id_state="1"
+
+        shift ;;
+
+      -n|--new-id)
+
+        export new_id_state="1"
+
+        shift ;;
+
+      -u|--user)
+
+        export user_state="1"
+
+        export user_name="${2}"
+
+        shift 2 ;;
+
+      --socks-port)
+
+        export socks_port_state=1
+
+        export socks_port_number="${2}"
+
+        shift 2 ;;
+
+      --control-port)
+
+        export control_port_state=1
+
+        export control_port_number="${2}"
+
+        shift 2 ;;
+
+      --proxy)
+
+        export proxy_state=1
+
+        export proxy_type="${2}"
+
+        shift 2 ;;
+
+      --haproxy)
+
+        export frontend_state=1
+
+        export frontend_type="haproxy"
+
+        shift 2 ;;
+
+      *)
+
+        if [[ "$2" == "-" ]] || [[ ! -z "$2" ]] ; then
+
+          printf "%s: invalid option -- '%s'\\n" "$_init_name" "$2"
+          _exit_ "1"
+
+        # elif [[ -z "$2" ]] ; then break ; fi
+        else break ; fi
+
+        ;;
+
+    esac
+
+  done
+
+  # If you run the script in debug mode, the information
+  # will be displayed on the screen from this point.
+  if [[ "$stdout_mode" == "debug" ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "starting debug mode"
+
+  fi
+
+  # Running tasks before start user functions.
+  _before_init
+
+  ################################# USER SPACE #################################
+  # ````````````````````````````````````````````````````````````````````````````
+  # Put here all your variable declarations, function calls
+  # and all the other code blocks.
+
+  # In this section we add external file (for -c|--config script param).
+  if [[ "$load_state" -eq 1 ]] ; then _load "head" "$config" ; fi
+
+  # shellcheck disable=SC2034
+  # Generate random value.
+  _random=$(date +"%s")
+
+  # Array that stores the names of variables used that are part of the script
+  # call parameters (_GETOPT_PARAMS). Useful when checking whether all
+  # or selected parameters without which the script can not work properly
+  # have been used. Do not add the load_state variable to the _opt_values array,
+  # which is supported above.
+  _opt_values=()
+
+  local _tproc=""
+  local _tor_processes_done=0
+  local _tor_processes_fail=0
+  local _tor_processes=0
+
+  local _pass_gen
+  local _pass_gen_ha
+  local _pass_hash
+
+  local _num='^[0-9]+$'
+
+  local _proxy_list=("polipo" "privoxy" "hpts" "node")
+
+  # Checking whether the variable value is a numeric value.
+  if [[ "$init_number" =~ $_num ]] || \
+     [[ "$socks_port_number" =~ $_num ]] || \
+     [[ "$socks_port_number" == "all" ]] || \
+     [[ "$control_port_number" =~ $_num ]] || \
+     [[ "$control_port_number" == "all" ]] ; then
+
+    # shellcheck disable=SC2034
+    _ports_limit=$((65536 - init_number))
+
+    # shellcheck disable=SC2154
+    if [[ "$init_state" -eq 1 ]] && [[ "$init_number" -le 0 ]] ; then
+
+      _sprintf "stop" "init_number is less or equal 0"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "init_number is less then or equal 0"
+
+    elif [[ "$socks_port_number" -ne "all" ]] || \
+         [[ "$control_port_number" -ne "all" ]] ; then
+
+      if [[ "$socks_port_state" -eq 1 && "$socks_port_number" -le 1023 ]] ; then
+
+        _sprintf "stop" "value is less then or equal 1023"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "value is less or equal 1023"
+
+      elif [[ "$control_port_state" -eq 1 && "$control_port_number" -le 1023 ]] ; then
+
+        _sprintf "stop" "value is less then or equal 1023"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "value is less or equal 1023"
+
+      elif [[ "$socks_port_state" -eq 1 && "$socks_port_number" -ge 65536 ]] ; then
+
+        _sprintf "stop" "value is equal or grather then 65536"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "value is equal or grather then 65536"
+
+      elif [[ "$socks_port_state" -eq 1 && "$control_port_number" -ge 65536 ]] ; then
+
+        _sprintf "stop" "value is equal or grather then 65536"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "value is equal or grather then 65536"
+
+      elif [[ "$socks_port_number" -gt "$_ports_limit" ]] || \
+           [[ "$control_port_number" -gt "$_ports_limit" ]] ; then
+
+        _sprintf "stop" "too many set processes"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "too many set processes"
+
+      elif [[ "$socks_port_number" -eq "$control_port_number" ]] ; then
+
+        _sprintf "stop" "value mismatch"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "value mismatch"
+
+      fi
+
+    fi
+
+  else
+
+    if [[ "$kill_state" -eq 1 ]] ; then
+
+      true
+
+    else
+
+      _sprintf "stop" "no numeric value"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "no numeric value"
+
+    fi
+
+  fi
+
+  # Checking if the user exists.
+  if [[ "$user_state" -eq 1 ]] ; then
+
+    if ! id "$user_name" >/dev/null 2>&1 ; then
+
+      _sprintf "stop" "incorrect user"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "incorrect user"
+
+    fi
+
+  fi
+
+  # Checking if proxy is set.
+  if [[ "$proxy_state" -eq 1 ]] ; then
+
+    if [[ "$proxy_type" == "socks" ]] || \
+       [[ " ${_proxy_list[*]} " =~ $proxy_type ]] ; then
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "correct proxy type: '$proxy_type'"
+
+    else
+
+      _sprintf "stop" "incorrect proxy type"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "incorrect proxy type"
+
+    fi
+
+  fi
+
+  # Checking if proxy is set for '--haproxy' param.
+  if [[ "$frontend_state" -eq 1 ]] ; then
+
+    if [[ "$proxy_state" -ne 1 ]] ; then
+
+      _sprintf "stop" "not set '--proxy' param"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "not set '--proxy' param"
+
+    fi
+
+  fi
+
+  # We set the value of the variable depending on the call option.
+  if [[ "$init_state" -eq 1 ]] ; then
+
+    local _opt_values=("init_state" "init_number" \
+                       "user_state" "user_name" \
+                       "socks_port_state" "socks_port_number" \
+                       "control_port_state" "control_port_number")
+
+  elif [[ "$kill_state" -eq 1 ]] ; then
+
+    local _opt_values=("kill_state")
+
+  elif [[ "$show_id_state" -eq 1 ]] || [[ "$new_id_state" -eq 1 ]] ; then
+
+    local _opt_values=("socks_port_state" "socks_port_number")
+
+  else
+
+    _sprintf "stop" "one of the required parameters was not found"
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "one of the required parameters was not found"
+
+  fi
+
+  # Checking the value of the variables (if they are unset or empty):
+  #   - variables for call parameters
+  #   - variables from the additional configuration files
+  if [[ "${#_opt_values[@]}" -ne 0 ]] ; then
+
+    for i in "${_opt_values[@]}" ; do
+
+      _i="" ; eval _i='$'"$i"
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "$i: '$_i'"
+
+      if [[ -z "$_i" ]] ; then
+
+        _sprintf "stop" "error of argument value: '$i' is unset or empty"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "error of argument value: '$i' is unset or empty"
+
+      fi
+
+    done
+
+  fi
+
+  if [[ "$init_state" -eq 1 ]] ; then
+
+    # First, we remove current processes.
+    # _tor_processes=($(pidof tor))
+    IFS=" " read -r -a _tor_processes <<< "$(pidof "tor")"
+
+    if [[ ${#_tor_processes[@]} -ne 0 ]] ; then
+
+      for _tpr in "${_tor_processes[@]}" ; do
+
+        # In this case, we can search for a specific identifier (${_tpr}).
+        # shellcheck disable=SC2009
+        _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$_tpr")
+
+        if [[ -z "$_tproc" ]] ; then
+
+          _logger "warn" \
+            "${_FUNCTION_ID}()" \
+            "not found or unknown tor process"
+
+        else
+
+          DestroyProcess "${_tpr}"
+
+        fi
+
+      done
+
+    else
+
+      _logger "warn" \
+        "${_FUNCTION_ID}()" \
+        "not found tor processes: ${_tpr}"
+
+    fi
+
+    # Removed all multitor process data directory.
+    find ${_multitor_directory}/* -delete >>"$_log_stdout" 2>&1
+
+    # Generate random password.
+    # Does storing the password in this form is security issue?
+    # shellcheck disable=SC2034
+    _pass_gen=$(< /dev/urandom tr -dc 'a-zA-Z0-9' | fold -w 18 | head -n 1)
+    # shellcheck disable=SC2034
+    _pass_hash=$(sudo -u "$user_name" tor --hash-password "$_pass_gen" | grep "16:")
+
+    if [[ "$proxy_state" -eq 1 ]] ; then
+
+      # shellcheck disable=SC2034,SC2154
+      local _tml_ha0="${_tml}/haproxy-template.cfg"
+      # shellcheck disable=SC2034,SC2154
+      local _tml_po0="${_tml}/${proxy_type}-template.cfg"
+
+      # shellcheck disable=SC2034,SC2154
+      local _tml_ha1="${_etc}/haproxy.cfg"
+      # shellcheck disable=SC2034,SC2154
+      local _tml_po1="${_etc}/${proxy_type}.cfg"
+
+      for _tfd in "$_tml_ha1" "$_tml_po1" ; do
+
+        # shellcheck disable=SC2154
+        if [[ -e "$_tfd" ]] ; then
+
+          rm -fr "${_tfd}"
+
+        fi
+
+      done
+
+      # Updated auth for HAProxy template configuration file.
+
+      # shellcheck disable=SC2154
+      cp "${_tml_ha0}" "${_tml_ha1}"
+
+      # shellcheck disable=SC2034
+      _pass_gen_ha=$(< /dev/urandom tr -dc 'a-zA-Z0-9' | fold -w 18 | head -n 1)
+
+      sed -i "s/__PASSWORD__/$_pass_gen_ha/g" "${_tml_ha1}"
+
+      unset _pass_gen_ha
+
+      _proxy_ports=()
+
+    else
+
+      # Flush etc/ directory.
+      rm -fr "${_etc:?}"/*
+
+    fi
+
+    for i in $(seq 0 $((init_number - 1))) ; do
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "socks_port_number: '$socks_port_number', control_port_number: '$control_port_number'"
+
+      CreateTorProcess "${user_name}" "${socks_port_number}" "${control_port_number}"
+
+      # For proxy:
+      _proxy_ports+=("$socks_port_number")
+
+      # In this case, we can search for a specific identifier (${socks_port_number}).
+      # shellcheck disable=SC2009
+      _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$socks_port_number")
+
+      # We get process information and display it on the screen.
+      GetTorProcess
+
+      # shellcheck disable=SC2154
+      if [[ "$socks_port_number" == "all" ]] ; then
+
+        _tor_pid_status="true"
+
+        if [[ "$verbose_mode" == "true" ]] ; then
+
+          echo
+          OutputGen
+
+        fi
+
+      elif [[ "$socks_port_number" -eq "$_tor_proc_socks" ]] ; then
+
+        _tor_pid_status="true"
+
+        if [[ "$verbose_mode" == "true" ]] ; then
+
+          echo
+          OutputGen
+
+        fi
+
+      else
+
+        _tor_pid_status="false"
+
+        printf "    \\e[1;30msocks_port\\e[m: \\e[1;39m%s\\e[m\\n" "$_tor_proc_socks"
+        printf "        \\e[1;30mstatus\\e[m: \\e[1;31m%s\\e[m\\n" "$_tor_pid_status"
+
+      fi
+
+      socks_port_number=$((socks_port_number + 1))
+      control_port_number=$((control_port_number + 1))
+
+    done
+
+    _proxy_processes_list=()
+
+    # shellcheck disable=SC2034
+    # Manages proxy processes stack.
+    for _proc in "haproxy" "${_proxy_list[@]}" ; do
+
+      CheckProxyProcess "${_proc}"
+
+    done
+
+    # shellcheck disable=SC2154
+    for _hproc in "${_proxy_processes_list[@]}" ; do
+
+      DestroyProcess "${_hproc}"
+
+    done
+
+    if [[ "$proxy_state" -eq 1 ]] ; then
+
+      CreateProxyProcess
+
+    fi
+
+    # shellcheck disable=SC2034
+    for _proc in "haproxy" "${_proxy_list[@]}" ; do
+
+      CheckProxyProcess "${_proc}"
+
+    done
+
+    printf "\\n     Set processes: \\e[0;39m%d\\e[m\\n" "$init_number"
+    printf "           Created: \\e[0;32m%d\\e[m\\n" "$_tor_processes_done"
+    printf "       Not created: \\e[0;31m%s\\e[m\\n" "$_tor_processes_fail"
+    printf "  Control password: \\e[2;38m%s\\e[m\\n\\n" "$_pass_gen"
+
+    # shellcheck disable=SC2154
+    if [[ ${#_proxy_processes_list[@]} -ne 0 ]] && [[ "$proxy_state" -eq 1 ]] ; then
+
+      if [[ "$proxy_type" == "socks" ]] ; then
+
+        printf "       Proxy state: \\e[2;32m%s\\e[m (haproxy » %s)\\n\\n" "running" "$proxy_type"
+
+      elif [[ "$frontend_type" == "haproxy" ]] ; then
+
+        printf "       Proxy state: \\e[2;32m%s\\e[m (haproxy » %s » socks)\\n\\n" "running" "$proxy_type"
+
+      else
+
+        printf "       Proxy state: \\e[2;32m%s\\e[m (%s » haproxy » socks)\\n\\n" "running" "$proxy_type"
+
+      fi
+
+    else
+
+      printf "       Proxy state: \\e[2;31m%s\\e[m (only tor)\\n\\n" "disable"
+
+    fi
+
+    # Unset password variables.
+    unset _pass_gen ; unset _pass_hash
+
+  elif [[ "$kill_state" -eq 1 ]] ; then
+
+    # First, we remove current processes.
+    # _tor_processes=($(pidof tor))
+    IFS=" " read -r -a _tor_processes <<< "$(pidof "tor")"
+
+    if [[ ${#_tor_processes[@]} -ne 0 ]] ; then
+
+      for _tpr in "${_tor_processes[@]}" ; do
+
+        # In this case, we can search for a specific identifier (${_tpr}).
+        # shellcheck disable=SC2009
+        _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$_tpr")
+
+        if [[ -z "$_tproc" ]] ; then
+
+          _logger "warn" \
+            "${_FUNCTION_ID}()" \
+            "not found or unknown tor process"
+
+        else
+
+          DestroyProcess "${_tpr}"
+
+          kill_status="1"
+
+        fi
+
+      done
+
+    else
+
+      _logger "warn" \
+        "${_FUNCTION_ID}()" \
+        "not found tor processes: ${_tpr}"
+
+    fi
+
+    # Removed all multitor process data directory.
+    find ${_multitor_directory}/* -delete >>"$_log_stdout" 2>&1
+
+    # shellcheck disable=SC2034,SC2154
+    local _tml_ha0="${_tml}/haproxy-template.cfg"
+    # shellcheck disable=SC2034,SC2154
+    local _tml_po0="${_tml}/${proxy_type}-template.cfg"
+
+    # shellcheck disable=SC2034,SC2154
+    local _tml_ha1="${_etc}/haproxy.cfg"
+    # shellcheck disable=SC2034,SC2154
+    local _tml_po1="${_etc}/${proxy_type}.cfg"
+
+    for _tfd in "$_tml_ha1" "$_tml_po1" ; do
+
+      # shellcheck disable=SC2154
+      if [[ -e "$_tfd" ]] ; then
+
+        rm -fr "${_tfd}"
+
+        kill_status="1"
+
+      fi
+
+    done
+
+    # Flush etc/ directory.
+    rm -fr "${_etc:?}"/*
+
+    # shellcheck disable=SC2034
+    # Manages proxy processes stack.
+    for _proc in "haproxy" "${_proxy_list[@]}" ; do
+
+      CheckProxyProcess "${_proc}"
+
+    done
+
+    # shellcheck disable=SC2154
+    for _hproc in "${_proxy_processes_list[@]}" ; do
+
+      DestroyProcess "${_hproc}"
+
+      kill_status="1"
+
+    done
+
+    if [[ "$kill_status" -eq 1 ]] ; then
+
+      printf "Multitor processes: \\e[1;31m%s\\e[m\\n" "stop"
+
+    fi
+
+  elif [[ "$show_id_state" -eq 1 ]] ; then
+
+    IFS=" " read -r -a _tor_processes <<< "$(pidof "tor")"
+
+    if [[ ${#_tor_processes[@]} -ne 0 ]] ; then
+
+      if [[ "$socks_port_number" == "all" ]] ; then
+
+        for _tpr in "${_tor_processes[@]}" ; do
+
+          # In this case, we can search for a specific identifier (${_tpr}).
+          # shellcheck disable=SC2009
+          _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$_tpr")
+
+          IFS=" " read -r -a _proc_socks_num_tmp <<< "$(echo "$_tproc" | awk '{print $13}')"
+
+          _proc_socks_num+=("${_proc_socks_num_tmp[@]}")
+
+        done
+
+        # IFS=$'\n' read -r -a _proc_socks_num_sorted <<< "$(sort <<<"${_proc_socks_num[*]}")"
+        # shellcheck disable=SC2207
+        _proc_socks_num_sorted=( $( printf "%s\\n" "${_proc_socks_num[@]}" | sort -n ) )
+
+        unset IFS
+
+        for _tpr in "${_proc_socks_num_sorted[@]}" ; do
+
+          # In this case, we can search for a specific identifier (${_tpr}).
+          # shellcheck disable=SC2009
+          _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$_tpr")
+
+          echo
+
+          # We get process information and display it on the screen.
+          GetTorProcess
+
+          if [[ "$socks_port_number" == "all" ]] ; then
+
+            _tor_pid_status="true"
+
+            OutputGen
+
+          elif [[ "$socks_port_number" -eq "$_tor_proc_socks" ]] ; then
+
+            _tor_pid_status="true"
+
+            OutputGen
+
+          else
+
+            _tor_pid_status="false"
+
+            printf "    \\e[1;30msocks_port\\e[m: \\e[1;39m%s\\e[m\\n" "$_tor_proc_socks"
+            printf "        \\e[1;30mstatus\\e[m: \\e[1;31m%s\\e[m\\n" "$_tor_pid_status"
+
+          fi
+
+        done
+
+      else
+
+        # In this case, we can search for a specific identifier (${socks_port_number}).
+        # shellcheck disable=SC2009
+        _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$socks_port_number")
+
+        if [[ -z "$_tproc" ]] ; then
+
+          _sprintf "stop" "not found tor process"
+
+          _logger "stop" \
+            "${_FUNCTION_ID}()" \
+            "not found tor process"
+
+        else
+
+          echo
+
+          # We get process information and display it on the screen.
+          GetTorProcess
+
+          if [[ "$socks_port_number" == "all" ]] ; then
+
+            _tor_pid_status="true"
+
+            OutputGen
+
+          elif [[ "$socks_port_number" -eq "$_tor_proc_socks" ]] ; then
+
+            _tor_pid_status="true"
+
+            OutputGen
+
+          else
+
+            _tor_pid_status="false"
+
+            _sprintf "stop" "not found tor process"
+
+            _logger "stop" \
+              "${_FUNCTION_ID}()" \
+              "not found tor process"
+
+          fi
+
+        fi
+
+      fi
+
+    else
+
+      _sprintf "stop" "not found tor process"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "not found tor process"
+
+    fi
+
+    echo
+
+  elif [[ "$new_id_state" -eq 1 ]] ; then
+
+    _output_tparams=()
+
+    if [[ "$socks_port_number" == "all" ]] ; then
+
+      IFS=" " read -r -a _tor_processes <<< "$(pidof "tor")"
+
+    else
+
+      _tor_processes=("$socks_port_number")
+
+    fi
+
+    # Password required at this stage.
+    read -rsp "Enter password: " _pass_in
+
+    echo
+
+    if [[ ${#_tor_processes[@]} -ne 0 ]] ; then
+
+      for _tpr in "${_tor_processes[@]}" ; do
+
+        # In this case, we can search for a specific identifier (${_tpr}).
+        # shellcheck disable=SC2009
+        _tproc=$(ps -ax | grep "multitor" | grep ".pid" | grep -v "grep" | grep "$_tpr")
+
+        if [[ -z "$_tproc" ]] ; then
+
+          _logger "warn" \
+            "${_FUNCTION_ID}()" \
+            "not found or unknown tor process"
+
+        else
+
+          # We get process information.
+          GetTorProcess
+
+          if [[ "$socks_port_number" == "all" ]] ; then
+
+            _tor_pid_status="true"
+
+          elif [[ "$socks_port_number" -eq "$_tor_proc_socks" ]] ; then
+
+            _tor_pid_status="true"
+
+          else
+
+            _tor_pid_status="false"
+
+          fi
+
+          # shellcheck disable=SC2154
+          _newnym=$(printf "AUTHENTICATE \"%s\"\\r\\nSIGNAL NEWNYM\\nQUIT\\n" "${_pass_in}" \
+          | nc 127.0.0.1 "$_tor_proc_control")
+
+          if echo "$_newnym" | grep -qi "250 closing connection" ; then
+
+            _identity_state="true"
+
+          elif echo "$_newnym" | grep -q "Authentication failed" ; then
+
+            _identity_state="false"
+
+          else
+
+            _identity_state="false"
+
+          fi
+
+          _output_tparams+=("$_tor_proc_id":"$_tor_proc_socks":"$_tor_proc_control":"$_identity_state")
+
+        fi
+
+      done
+
+    else
+
+      _sprintf "stop" "not found tor process"
+
+      _logger "stop" \
+        "${_FUNCTION_ID}()" \
+        "not found tor process"
+
+    fi
+
+    unset _pass_in
+
+    for i in "${_output_tparams[@]}" ; do
+
+      # shellcheck disable=SC2034
+      _key_id=$(echo "$i" | awk -v FS="(:|:)" '{print $1}')
+      # shellcheck disable=SC2034
+      _key_socks=$(echo "$i" | awk -v FS="(:|:)" '{print $2}')
+      # shellcheck disable=SC2034
+      _key_control=$(echo "$i" | awk -v FS="(:|:)" '{print $3}')
+      # shellcheck disable=SC2034
+      _key_state=$(echo "$i" | awk -v FS="(:|:)" '{print $4}')
+
+      if [[ "$_key_state" == "true" ]] ; then
+
+        printf "New identity for \\e[1;39m%d\\e[m: \\e[0;32m%s\\e[m\\n" "$_key_id" "regenerated"
+
+      else
+
+        if [[ "$_tor_pid_status" == "false" ]] ; then
+
+          _logger "warn" \
+            "${_FUNCTION_ID}()" \
+            "not found or unknown tor process"
+
+          printf "New identity for \\e[1;39m%d\\e[m: \\e[0;31m%s\\e[m\\n" "$_key_id" "unknown"
+
+        else
+
+          printf "New identity for \\e[1;39m%d\\e[m: \\e[0;31m%s\\e[m\\n" "$_key_id" "failed"
+
+        fi
+
+      fi
+
+    done
+
+  fi
+
+  # ````````````````````````````````````````````````````````````````````````````
+
+  if [[ "$time_tracking" == "true" ]] ; then
+
+    # Counting the execution time.
+    _endtime=$(date +%s)
+    _totaltime=$((_endtime - _begtime))
+
+    # Print time header.
+    printf '\\e[m\\e[1;39mTOTAL TIME: %dh:%dm:%ds\\e[m\n' \
+            $((_totaltime/3600)) $((_totaltime%3600/60)) $((_totaltime%60))
+
+  fi
+
+  return "$_STATE"
+
+}

+ 723 - 0
src/helpers

@@ -0,0 +1,723 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+################################################################################
+####################### Definitions of global functions ########################
+################################################################################
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _exit_()
+#
+# Description:
+#   Covers the default exit command.
+#
+# Usage:
+#   _exit_ value
+#
+# Examples:
+#   _exit_ 0
+#
+
+function _exit_() {
+
+  local _FUNCTION_ID="_exit_"
+  local _STATE="0"
+
+  _STATUS="$1"
+
+  # Remember that for it a trap is executed that intercepts
+  # the exit command (at the end of this function).
+  if [[ "$_STATUS" -eq 0 ]] ; then
+
+    # Add tasks when exiting the code is equal 0.
+    true
+
+  else
+
+    # Add tasks when exiting the code is non equal 0.
+    false
+
+  fi
+
+  exit "$_STATUS"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _get_trap_SIG()
+#
+# Description:
+#   Ensuring they always perform necessary cleanup operations,
+#   even when something unexpected goes wrong. It can handle
+#   all output signals.
+#
+# Usage:
+#   trap _get_trap_SIG SIGNAL
+#
+# Examples:
+#   trap _get_trap_SIG EXIT
+#   trap "_get_trap_SIG SIGS" SIGHUP SIGTERM
+#
+
+function _get_trap_SIG() {
+
+  local _FUNCTION_ID="_get_trap_SIG"
+  local _STATE="${_STATUS:-}"
+
+  local _SIG_type="$1"
+
+  # Remember not to duplicate tasks in the _exit_() and _get_trap_SIG()
+  # functions. Tasks for the _exit_() function only work within it
+  # and refer to the exit mechanism. Tasks in the _get_trap_SIG() function
+  # can refer to specific signal or all signals.
+
+  if [ -z "$_STATE" ] ; then _STATE=254
+
+  # Performs specific actions for the EXIT signal.
+  elif [[ "$_SIG_type" == "EXIT" ]] ; then
+
+    # Unset variables (e.g. global):
+    #   - local _to_unset=("$IFS_ORIG" "$IFS_HACK" "$IFS" "$PATH")
+    local _to_unset=("$PATH")
+
+    # Running tasks before the end of the script.
+    _after_init
+
+    # shellcheck disable=SC2034
+    for i in "${_to_unset[@]}" ; do unset i ; done
+
+    # You can cover the code supplied from the _exit_() function
+    # (in this case) or set a new one.
+    _STATE="${_STATUS:-}"
+
+  # Performs specific actions fot the other signals.
+  # In this example, using the SIGS string, we mark several output signals
+  # (see the second example in the description of the function).
+  elif [[ "$_SIG_type" == "SIGS" ]] ; then
+
+    # You can cover the code supplied from the function
+    # or set a new one.
+    _STATE="${_STATUS:-}"
+
+  else
+
+    # In this block the kill command was originally used,
+    # however, it suspended the operation of dracnmap.
+    # The lack of this command terminates the process
+    # and does not cause the above problems.
+    _STATE="255"
+
+  fi
+
+  _logger "exit" \
+    "$_FUNCTION_ID > ${_SIG_type} (${_STATE})"
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _logger()
+#
+# Description:
+#   Saving selected operation states to a log file
+#   and allows you to terminate the script with 'stop' signal.
+#
+#   Four states of message type:
+#     info - normal information
+#     head - normal information (header)
+#     warn - warning information
+#     stop - interrupts script execution
+#
+# Usage:
+#   _logger "type" "message"
+#
+# Examples:
+#   _logger "info" "load config file properly"
+#   _logger "stop" "not connected"
+#
+
+function _logger() {
+
+  local _FUNCTION_ID="_logger"
+  local _STATE="0"
+
+  local _type="$1"
+  local _to_log=""
+  local _conv_type=""
+
+  _to_log=$(shift ; echo "$@")
+  _conv_type=$(echo "$_type" | tr '[:lower:]' '[:upper:]')
+
+  # shellcheck disable=SC2154
+  if [[ ! -d "$_log_directory" && ! -L "$_log_directory" ]] ; then
+    mkdir -p "$_log_directory" ; fi
+
+  # Normal debug mode (output the same as the contents of the log file).
+  # shellcheck disable=SC2154
+  if [[ "$stdout_mode" == "debug" ]] ; then
+
+    printf "%s  %s:  [%s] %s\\n" \
+           "$(date +"%d/%m/%y %X")" \
+           "$_init_name" \
+           "$_conv_type" \
+           "$_to_log" \
+           | tee -a "$_log_path"
+
+  # The decision whether an INFO is to be only log to a file
+  # or to a file and to standard output.
+  else
+
+    printf "%s  %s:  [%s] %s\\n" \
+           "$(date +"%d/%m/%y %X")" \
+           "$_init_name" \
+           "$_conv_type" \
+           "$_to_log" \
+           >>"$_log_path"
+
+  fi
+
+  # By means of this construction, we can terminate the operation
+  # of the script with the action of logging into the log file.
+  # This do not have to remember to place the _exit_ <value> function
+  # in 'exit' script points. If you prefer to have more control,
+  # do not use the _logger function with the 'stop' parameter.
+  if [[ "$_type" == "stop" ]] ; then _exit_ 255 ; fi
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _sprintf()
+#
+# Description:
+#   Function designed to output to the screen in a clear format.
+#
+# Usage:
+#   _sprintf "type" "message"
+#
+# Examples:
+#   _sprintf "head" "correct certificate: $_ssl_cert_file"
+#
+
+function _sprintf() {
+
+  local _FUNCTION_ID="_sprintf"
+  local _STATE="0"
+
+  local _s_type="$1"
+  local _s_info="$2"
+
+  # Determine the type of character and color for each type
+  # of output information.
+  if [[ "$_s_type" == "head" ]] ; then
+
+    s_char="+"
+    s_trgb="1;32"
+
+  elif [[ "$_s_type" == "info" ]] ; then
+
+    s_char="-"
+    s_trgb="0;33"
+
+  elif [[ "$_s_type" == "warn" ]] ; then
+
+    s_char="!"
+    s_trgb="1;37"
+
+  elif [[ "$_s_type" == "stop" ]] ; then
+
+    s_char="!"
+    s_trgb="1;31"
+
+  else
+
+    s_char="-"
+    s_trgb="0;37"
+
+  fi
+
+  # If you run the tool in verbose mode do not display output using _sprintf.
+  if [[ "$stdout_mode" != "debug" ]] ; then
+
+    if [[ "$_s_type" == "spin" ]] && [[ ! -z "$_s_info" ]] ; then
+
+      # Process id of the previous running command.
+      local _pid="$_s_info"
+
+      local _sc='-\|/'
+
+      # Verify that the process is still running.
+      local _n="0"
+
+      # shellcheck disable=SC2143
+      while [[ $(ps a | awk '{print $1}' | grep -w "$_pid") ]] ; do
+
+        _n=$(( ( _n + 1 ) % 4 ))
+        printf "\\r[%s]" "${_sc:_n:1}"
+        sleep 0.1
+
+      done
+
+      # If the end, we clean.
+      printf "\\r"
+
+    else
+
+      # Normal execution if:
+      # - spinner has not been called
+      # - spinner completed
+
+      # If verbose mode is enabled, display info message.
+      # shellcheck disable=SC2154
+      if [[ "$printf_mode" == "verbose" ]] && [[ "$_s_type" == "info" ]] ; then
+
+        printf '[\e['${s_trgb}'m%s\e[m] %s\n' "$s_char" "$_s_info"
+
+      else
+
+        # If not, just display only the head, warn or stop string.
+        # shellcheck disable=SC2154
+        if [[ "$_s_type" == "head" ]] ; then
+
+          if [[ "$s_color" == "true" ]] ; then
+
+            c_trgb="1;39"
+
+            printf '[\e['${s_trgb}'m%s\e[m] \e['${c_trgb}'m%s\e[m\n' "$s_char" "$_s_info"
+
+          else
+
+            printf '[\e['${s_trgb}'m%s\e[m] %s\n' "$s_char" "$_s_info"
+
+          fi
+
+        elif [[ "$_s_type" == "warn" ]] ; then
+
+          if [[ "$s_color" == "true" ]] ; then
+
+            c_trgb="1;43"
+
+            printf '[\e['${s_trgb}'m%s\e[m] \e['${c_trgb}'m%s\e[m\n' "$s_char" "$_s_info"
+
+          else
+
+            printf '[\e['${s_trgb}'m%s\e[m] %s\n' "$s_char" "$_s_info"
+
+          fi
+
+        elif [[ "$_s_type" == "stop" ]] ; then
+
+          if [[ "$s_color" == "true" ]] ; then
+
+            c_trgb="1;41"
+
+            printf '[\e['${s_trgb}'m%s\e[m] \e['${c_trgb}'m%s\e[m\n' "$s_char" "$_s_info"
+
+          else
+
+            printf '[\e['${s_trgb}'m%s\e[m] %s\n' "$s_char" "$_s_info"
+
+          fi
+
+        fi
+
+      fi
+
+    fi
+
+  fi
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _init_cmd()
+#
+# Description:
+#   Function executing given as a command parameter.
+#
+# Usage:
+#   _init_cmd "parameter"
+#
+# Examples:
+#   _init_cmd "eval cd /etc/init.d && ls"
+#
+
+function _init_cmd() {
+
+  local _FUNCTION_ID="_init_cmd"
+  local _STATE="0"
+
+  local _cmd="$1"
+
+  # Uncomment if you want to display executed commands.
+  # _sprintf "info" "init: \"$_cmd\""
+
+  _logger "info" \
+    "${_FUNCTION_ID}()" \
+    "init: $_cmd"
+
+  # Execute command and exit save to file.
+  # shellcheck disable=SC2154
+  $_cmd >>"$_log_stdout" 2>&1 &
+
+  # We keep pid of the last command.
+  _pid="$!"
+
+  # When the '(command) &' command is performed.
+  _sprintf "spin" "$_pid"
+
+  # Very important line:
+  # We define the state of the output job from the background.
+  wait "$_pid" &>/dev/null && _state="0" || _state="1"
+
+  if [[ "$_state" -eq 0 ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "result: pass"
+
+  else
+
+    _sprintf "stop" "result: fail"
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "result: fail"
+
+  fi
+
+  _STATE="$_state"
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _init_function()
+#
+# Description:
+#   It deals with launching functions:
+#     - creates a simple environment for the functions you perform
+#     - operates on message to display and command to execute
+#
+# Usage:
+#   _init_function function_name params
+#
+# Examples:
+#   _init_function "CheckConn 172.20.20.50 22"
+#
+
+function _init_function() {
+
+  local _FUNCTION_ID="_init_function"
+  local _STATE="0"
+
+  local _args=("$@")
+
+  local _cmd_begtime="0"
+  local _cmd_endtime="0"
+  local _cmd_totaltime="0"
+
+  _logger "info" \
+    "${_FUNCTION_ID}()" \
+    "init function: '${_args[*]}'"
+
+  # In these two variables we store the message to display
+  # and the command to execute.
+  export _msg_args=()
+  export _cmd_args=()
+
+  eval "${_args[@]}" ; if [ ! $? ] ; then _exit_ 255 ; fi
+
+  _num="${#_msg_args[@]}"
+  for (( _xa="0" ; _xa<=((_num - 1)) ; _xa++ )) ; do
+
+    _msg="${_msg_args[$_xa]}"
+    _full_command="${_cmd_args[$_xa]}"
+
+    _sprintf "info" "$_msg"
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "$_msg"
+
+    _cmd_begtime=$(date +%s)
+
+    _init_cmd "$_full_command" ; if [ ! "$?" ] ; then _exit_ 255 ; fi
+
+    _cmd_endtime=$(date +%s)
+
+    # shellcheck disable=SC2154
+    if [[ "$time_mode" -eq 1 ]] && [[ "$_cmdtime_state" -eq 1 ]] ; then
+
+      _cmd_totaltime=$((_cmd_endtime - _cmd_begtime))
+
+      _cmd_totaltime_out=$(printf '%dh:%dm:%ds' \
+      $((_cmd_totaltime/3600)) $((_cmd_totaltime%3600/60)) $((_cmd_totaltime%60)))
+
+      _sprintf "info" "time: $_cmd_totaltime_out"
+
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "time: $_cmd_totaltime_out"
+
+    fi
+
+  done
+
+  _cmdtime_state="0"
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _load()
+#
+# Description:
+#   Responsible for loading the configuration file, $config variable
+#   parameter is defined in the script call.
+#
+# Usage:
+#   _load "type" "path_to_config_file"
+#
+# Examples:
+#   _load "info" "$config"
+#   _load "head" "/tmp/file.cfg"
+#
+
+function _load() {
+
+  local _FUNCTION_ID="_load"
+  local _STATE="0"
+
+  local _type="$1"
+  local _filename="$2"
+
+  if [[ ! -z "$_filename" ]] && [[ -e "$_filename" ]] ; then
+
+    # If we do not want to inform that the file is loaded,
+    # the value is 'null', otherwise:
+    if [[ "$_type" == "head" ]] ; then
+
+      _sprintf "head" "load configuration"
+      _sprintf "info" "file: '$_filename'"
+
+    elif [[ "$_type" == "info" ]] ; then
+
+      _sprintf "info" "load configuration: '$_filename'"
+
+    fi
+
+    # shellcheck disable=SC1090
+    # If the file exists is loaded.
+    . "$_filename" && \
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "configuration file: '$_filename'"
+
+  elif [ -z "$_filename" ] ; then
+
+    _sprintf "stop" "incorrectly loaded '$_filename' file (incorrect filename)"
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "incorrectly loaded '$_filename' file (incorrect filename)"
+
+  else
+
+    _sprintf "stop" "incorrectly loaded '$_filename' file (does not exist?)"
+
+    _logger "stop" \
+      "${_FUNCTION_ID}()" \
+      "incorrectly loaded '$_filename' file (does not exist?)"
+
+  fi
+
+  return "$_STATE"
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _help_()
+#
+# Description:
+#   Help message. Should be consistent with the contents of the file README.md.
+#
+# Usage:
+#   _help_
+#
+# Examples:
+#   _help_
+#
+
+function _help_() {
+
+  local _FUNCTION_ID="_help_"
+  local _STATE=0
+
+  printf "%s" "
+  Usage:
+    $_init_name <option|long-option>
+
+  Examples:
+    $_init_name --init 2 --user debian-tor --socks-port 9000 --control-port 9900
+    $_init_name --init 10 --user debian-tor --socks-port 9000 --control-port 9900 --proxy socks
+    $_init_name --show-id --socks-port 9000
+
+  Options:
+        --help                        show this message
+        --debug                       displays information on the screen (debug mode)
+        --verbose                     displays more information about TOR processes
+    -i, --init <num>                  init new tor processes
+    -k, --kill                        kill all multitor processes
+    -s, --show-id                     show specific tor process id
+    -n, --new-id                      regenerate tor circuit
+    -u, --user <string>               set the user (only with -i|--init)
+        --socks-port <port_num|all>   set socks port number
+        --control-port <port_num>     set control port number
+        --proxy <proxy_type>          set socks or http (polipo, privoxy, hpts) proxy server
+        --haproxy                     set HAProxy as a frontend for http proxies (only with --proxy)
+
+
+  This program comes with ABSOLUTELY NO WARRANTY.
+  This is free software, and you are welcome to redistribute it
+  under certain conditions; for more details please see
+  <http://www.gnu.org/licenses/>.
+
+"
+
+  return $_STATE
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _before_init()
+#
+# Description:
+#   INFOs performed before calling the __main__ function, e.g.
+#   attaching files, cleaning logs (if you need a function that will deal
+#   with it, put it here).
+#
+# Usage:
+#   _before_init
+#
+# Examples:
+#   _before_init
+#
+
+function _before_init() {
+
+  local _FUNCTION_ID="_before_init"
+  local _STATE=0
+
+  # shellcheck disable=2154
+  cd "$_init_directory" || \
+  _logger "stop" \
+    "${_FUNCTION_ID}()" \
+    "directory change error: '$_init_directory'"
+
+  : >"$_log_stdout"
+
+  # shellcheck disable=2154
+  if [[ ! -d "$_multitor_directory" && ! -L "$_multitor_directory" ]] ; then
+
+    _logger "info" \
+      "${_FUNCTION_ID}()" \
+      "creating a multitor directory: '$_multitor_directory'"
+
+    mkdir -m 0700 "$_multitor_directory" >>"$_log_stdout" 2>&1
+    _kstate="$?"
+
+    if [[ $_kstate -eq 0 ]] ; then
+
+      # shellcheck disable=2154
+      _logger "info" \
+        "${_FUNCTION_ID}()" \
+        "created tor process directory: ${_multitor_directory}"
+
+      chown -R "${user_name}" "${_multitor_directory}" >>"$_log_stdout" 2>&1
+      _kstate="$?"
+
+      if [[ $_kstate -eq 0 ]] ; then
+
+        _logger "info" \
+          "${_FUNCTION_ID}()" \
+          "changed owner properly to: ${_multitor_directory}"
+
+      else
+
+        _logger "warn" \
+          "${_FUNCTION_ID}()" \
+          "the owner could not be changed"
+
+      fi
+
+    else
+
+      _logger "warn" \
+        "${_FUNCTION_ID}()" \
+        "not created tor process directory"
+
+    fi
+
+  else
+
+    _multitor_directory_owner=$(stat -c %U "$_multitor_directory")
+
+    if [[ "$init_state" -eq 1 ]] ; then
+
+      if [[ "$_multitor_directory_owner" != "$user_name" ]]  ; then
+
+        _sprintf "stop" "bad multitor directory owner"
+
+        _logger "stop" \
+          "${_FUNCTION_ID}()" \
+          "bad multitor directory owner"
+
+      fi
+
+    fi
+
+  fi
+
+  # Flush etc/ directory.
+  rm -fr "${_etc:?}"/*
+
+  return $_STATE
+
+}
+
+# ``````````````````````````````````````````````````````````````````````````````
+# Function name: _after_init()
+#
+# Description:
+#   INFOs performed after calling the __main__ function, e.g.
+#   cleaning logs (if you need a function that will deal with it,
+#   put it here).
+#
+# Usage:
+#   _after_init
+#
+# Examples:
+#   _after_init
+#
+
+function _after_init() {
+
+  local _FUNCTION_ID="_after_init"
+  local _STATE=0
+
+  cd "$_init_directory" || \
+  _logger "stop" \
+    "${_FUNCTION_ID}()" \
+    "directory change error: '$_init_directory'"
+
+  return $_STATE
+
+}

+ 47 - 0
src/import

@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+################################################################################
+#################### External variables/functions and libs #####################
+################################################################################
+
+# Set of external files with variables, functions and other. The configuration
+# of each of them is in lib directory. The fd_stack array stores the names of
+# attached files. If you create a new file with some interesting function,
+# name it appropriately and insert to the array.
+# Example:
+#   - lib/kill_process - file with your function
+#   - _fd_stack=("kill_process") - says to include this file in the script
+readonly _fd_stack=("CheckConn" "CreateTorDirectory" "CreateTorProcess" \
+                    "DestroyProcess" "GetTorProcess" "CheckProxyProcess" \
+                    "CreateProxyProcess" "CreateWebProxyProcess" "OutputGen")
+
+if [[ "${#_fd_stack[@]}" -ne 0 ]] ; then
+
+  for _fd in "${_fd_stack[@]}" ; do
+
+    # shellcheck disable=SC2154
+    _fd_full_path="${_lib}/${_fd}"
+
+    if [[ ! -z "$_fd_full_path" ]] && [[ -e "$_fd_full_path" ]] ; then
+
+      # If the file exists is loaded.
+      # shellcheck disable=SC1090
+      source "$_fd_full_path"
+
+    elif [[ -z "$_fd_full_path" ]] ; then
+
+      printf "incorrectly loaded '%s' file (incorrect filename)" "$_fd_full_path"
+      _exit_ "1"
+
+    else
+
+      printf "incorrectly loaded '%s' file (does not exist?)" "$_fd_full_path"
+      _exit_ "1"
+
+    fi
+
+  done
+
+fi

+ 35 - 0
src/settings

@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+# shellcheck shell=bash
+
+################################################################################
+################## The configuration parameters of the script ##################
+################################################################################
+
+# Bash 'Strict Mode':
+#   errexit  - exit the script if any statement returns a non-true return value
+#   pipefail - exit the script if any command in a pipeline errors
+#   nounset  - exit the script if you try to use an uninitialised variable
+#   xtrace   - display debugging information
+set -o pipefail
+
+# Internal field separator (more flexible):
+#   IFS_ORIG="$IFS"
+#   IFS_HACK=$'\n\t'
+#   IFS="$IFS_HACK"
+
+# PATH env variable setup:
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+# Setting permissions in the script environment:
+#   0022 - less restrictive settings (default value)
+#   0027 - for better security than above
+#   0077 - only for user access (more restrictive)
+umask 0027
+
+# Catch the listed SIGNALS, which may be signal names with or without the SIG
+# prefix, or signal numbers. By default, only the signal 0 or EXIT is supported.
+trap "_get_trap_SIG EXIT" EXIT
+
+# shellcheck disable=SC2173
+# trap "_get_trap_SIG SIGS" SIGHUP SIGTERM SIGKILL SIGINT

BIN
static/img/multitor_output_1.png


+ 51 - 0
static/man8/multitor.8

@@ -0,0 +1,51 @@
+.\" Manpage for multitor.
+.\" Contact trimstray@gmail.com.
+.TH man 8 "21.01.2018" "1.3.0" "multitor man page"
+.SH NAME
+multitor \- create multiple TOR instances
+.SH SYNOPSIS
+multitor <option|long-option>
+.SH DESCRIPTION
+It's provides one single endpoint for clients. In addition, you can view previously running TOR processes and create a new identity for all or selected processes.
+.SH OPTIONS
+--help
+        show this message
+
+--debug
+        displays information on the screen (debug mode)
+
+--verbose
+        displays more information about TOR processes
+
+-i, --init <num>
+        init new tor processes
+
+-k, --kill
+        kill all multitor processes
+
+-s, --show-id
+        show specific tor process id
+
+-n, --new-id
+        regenerate tor circuit
+
+-u, --user <string>
+        set the user (only with -i|--init)
+
+--socks-port <port_num|all>
+        set socks port number
+
+--control-port <port_num>
+        set control port number
+
+--proxy <proxy_type>
+        set socks or http (polipo, privoxy, hpts) proxy server
+
+--haproxy
+        set HAProxy as a frontend for http proxies (only with --proxy)
+.SH SEE ALSO
+https://github.com/trimstray/multitor
+.SH BUGS
+No known bugs.
+.SH AUTHOR
+trimstray (trimstray@gmail.com)

+ 33 - 0
templates/haproxy-template.cfg

@@ -0,0 +1,33 @@
+global
+  daemon
+  maxconn           500
+
+defaults proxy
+  mode              tcp
+  timeout connect   5000ms
+  timeout client    15000ms
+  timeout server    15000ms
+  retries           0
+
+frontend tor_proxies
+  # Less restrictive settings:
+  # - bind :16379 name proxy
+  bind              127.0.0.1:16379 name proxy
+  default_backend   bk_node
+
+listen stats
+  # URL: http://127.0.0.1:16380/stats
+  bind              127.0.0.1:16380
+  mode              http
+  stats             enable
+  stats             hide-version
+  stats             realm Haproxy\ Statistics
+  stats             uri /stats
+  # stats           auth ha_admin:__PASSWORD__
+
+backend bk_node
+  log               global
+  # Other: leastconn
+  balance           roundrobin
+
+# SOCKS/HTTP backends:

+ 23 - 0
templates/polipo-template.cfg

@@ -0,0 +1,23 @@
+daemonise = true
+
+logSyslog = true
+logFile = /var/log/polipo/polipo.log
+
+# Ignore s-maxage and private.
+cacheIsShared = false
+# Default cache location: /var/cache/polipo
+diskCacheRoot = /dev/null
+# Don't cache redirects.
+dontCacheRedirects = true
+# Don't cache cookies.
+dontCacheCookies = true
+
+# We allow connections to local services (HAProxy).
+allowedClients = 127.0.0.1
+
+# The IP address on which the proxy listens.
+proxyAddress = "127.0.0.1"
+
+socksProxyType = socks5
+
+# SOCKS parent proxy:

+ 24 - 0
templates/privoxy-template.cfg

@@ -0,0 +1,24 @@
+user-manual /usr/share/doc/privoxy/user-manual
+
+confdir /etc/privoxy
+
+logdir /var/log/multitor/privoxy
+
+actionsfile match-all.action
+actionsfile default.action
+actionsfile user.action
+# filterfile default.filter
+# filterfile user.filter
+
+toggle 1
+enable-remote-toggle 0
+enable-remote-http-toggle 0
+enable-edit-actions 0
+enforce-blocks 0
+buffer-limit 4096
+forwarded-connect-retries 0
+accept-intercepted-requests 0
+allow-cgi-request-crunching 0
+split-large-forms 0
+
+# SOCKS parent proxy: