lib/gsmlg/tor.ex

defmodule GSMLG.Tor do
  @moduledoc """
  Documentation for `GSMLG.Tor`.
  """

  @doc """
  Start tor server at

  - `127.0.0.1:9050`
  - `[::1]:9050`

  Accept accesss from `127.0.0.1/8`

  Start server with

  ```
  GSMLG.Tor.start()
  ```

  To start server, libevent must be installed.
  """
  def start() do
    File.mkdir_p(Application.app_dir(:gsmlg_tor, "priv") <> "/etc")
    f = Application.app_dir(:gsmlg_tor, "priv") <> "/etc/torrc"
    File.write!(f, torrc())

    System.cmd(cmd(), ["-f", f],
      into: IO.stream(),
      cd: Application.app_dir(:gsmlg_tor, "priv") <> "/tor"
    )
  end

  def torrc() do
    """
    ##
    ## OPNsense autogenerated config file.
    ## Don't change it because your changes get lost.
    ##
    ##

    SOCKSPort 127.0.0.1:9050 # localhost IPv4
    SOCKSPort [::1]:9050     # localhost IPv6

    SOCKSPolicy accept 127.0.0.1/8

    SOCKSPolicy reject *
    SOCKSPolicy reject6 *


    Scheduler KISTLite,Vanilla

    DataDirectory #{Application.app_dir(:gsmlg_tor, "priv")}/data

    FascistFirewall 0


    ## Client Authentication

    HardwareAccel 1

    """
  end

  def cmd() do
    p = Application.app_dir(:gsmlg_tor, "priv")
    tor_cmd = p <> "/tor/tor"

    unless File.exists?(tor_cmd) do
      IO.puts("cmd not exists, downloading...")

      case download() do
        0 -> tor_cmd
        _ -> :error
      end
    else
      tor_cmd
    end
  end

  @doc """
  Download tor from website.

  https://www.torproject.org/download/tor/

  linux x86_64 is `https://www.torproject.org/dist/torbrowser/12.0/tor-expert-bundle-12.0-linux-x86_64.tar.gz`

  ## Examples

      iex> GSMLG.Tor.download()
      0

  """
  def download do
    body =
      case System.fetch_env("HTTP_PROXY") do
        {:ok, proxy} ->
          %HTTPoison.Response{status_code: 200, body: body} =
            download_url() |> HTTPoison.get!([], proxy: proxy, follow_redirect: true)

          body

        :error ->
          %HTTPoison.Response{status_code: 200, body: body} =
            download_url() |> HTTPoison.get!([], follow_redirect: true)

          body
      end

    p = Application.app_dir(:gsmlg_tor, "priv")
    f = p <> "/" <> filename()

    File.write!(f, body)

    {_, code} = System.cmd("tar", ["zxf", f, "-C", p])

    code
  end

  def download_url() do
    "https://dist.torproject.org/torbrowser/12.0/" <> filename()
  end

  def filename() do
    {os, arch} = os_arch()
    "tor-expert-bundle-12.0-#{os}-#{arch}.tar.gz"
  end

  def os_arch() do
    {arch, os_str} =
      case :erlang.system_info(:system_architecture) |> to_string() do
        "aarch64-" <> os_info ->
          {"aarch64", os_info}

        "x86_64-" <> os_info ->
          {"x86_64", os_info}

        _ ->
          IO.puts("unsupported")
          :error
      end

    {ostype} =
      cond do
        String.contains?(os_str, "darwin") -> {"macos"}
        String.contains?(os_str, "linux") -> {"linux"}
        true -> :error
      end

    {ostype, arch}
  end
end