class Tor::HiddenService

Attributes

options[R]

Public Class Methods

new(options={}) click to toggle source
# File lib/tor/hidden-service.rb, line 10
def initialize(options={})
  @options = parse_options(options)
  @base_dir = "#{@options[:temp_dir]}/tor_#{@options[:tor_control_port]}"
end

Public Instance Methods

hostname() click to toggle source
# File lib/tor/hidden-service.rb, line 40
def hostname
  return nil unless (@tor_pid and (hostname = File.read(open("#{@base_dir}/hidden_service/hostname"))))
  return hostname.strip
end
start() click to toggle source
# File lib/tor/hidden-service.rb, line 15
def start
  generate_tor_config

  @pid = fork do
    spawn @options[:tor_executable], '-f',  "#{@base_dir}/torrc"
  end
  Process.wait(@pid)

  begin
    @tor_pid = File.read(open("#{@base_dir}/pid")).strip.to_i
  rescue Errno::ENOENT
    log_message "Waiting for Tor PID to appear..."
    sleep 1
    retry
  end

  log_message "Started Tor with PID #{@tor_pid} on control port #{@options[:tor_control_port]}"

  at_exit do
    log_message "Killing Tor PID #{@tor_pid}..."
    Process.kill :SIGTERM, @tor_pid
    Process.wait
  end
end

Private Instance Methods

generate_tor_config() click to toggle source
# File lib/tor/hidden-service.rb, line 71
def generate_tor_config
  tor_config = {
    DataDirectory:     "#{@base_dir}/data",
    ControlPort:       "#{@options[:tor_control_port]}",
    HiddenServiceDir:  "#{@base_dir}/hidden_service",
    HiddenServicePort: "#{@options[:hidden_service_port]} #{@options[:server_host]}:#{@options[:server_port]}",
    PidFile:           "#{@base_dir}/pid",
    RunAsDaemon:       1,
    SocksPort:         0
  }

  begin
    FileUtils.mkdir_p tor_config[:DataDirectory]
    FileUtils.mkdir_p tor_config[:HiddenServiceDir]
    File.write("#{tor_config[:HiddenServiceDir]}/private_key", @options[:private_key])
    FileUtils.chmod 0700, tor_config[:HiddenServiceDir]
    FileUtils.chmod 0600, "#{tor_config[:HiddenServiceDir]}/private_key"

    File.write(
      "#{@base_dir}/torrc",
      tor_config.map{|k,v| "#{k} #{v}"}.join("\n"))
  rescue => e
    raise "Error creating configuration: #{e}"
  end

  return true
end
log_error(message) click to toggle source
# File lib/tor/hidden-service.rb, line 103
def log_error(message)
  $stderr.puts "Tor::HiddenService: [#{@options[:tor_control_port]}]: ERROR: #{message}"
end
log_message(message) click to toggle source
# File lib/tor/hidden-service.rb, line 99
def log_message(message)
  $stdout.puts "Tor::HiddenService: [#{@options[:tor_control_port]}]: #{message}"
end
parse_options(options) click to toggle source
# File lib/tor/hidden-service.rb, line 47
def parse_options(options)
  parsed_options = {
    tor_executable:      (Tor.available? ? Tor.program_path : nil),
    temp_dir:            "#{ENV['PWD']}/tmp" || nil,
    private_key:         nil,
    server_host:         'localhost',
    server_port:         ENV['PORT'],
    hidden_service_port: 80,
    tor_control_port:    rand(10000..65000)
  }.merge(options)

  raise ArgumentError, "No tor executable found" unless parsed_options[:tor_executable]
  raise ArgumentError, "temp_dir #{parsed_options[:temp_dir]} does not exist or is not writable" unless (
    File.writable? parsed_options[:temp_dir] or
    FileUtils.mkdir_p parsed_options[:temp_dir])
  raise ArgumentError, "Private key is not a valid 1024 bit RSA private key" unless (
    parsed_options[:private_key] and
    OpenSSL::PKey::RSA.new(parsed_options[:private_key]).private? and
    OpenSSL::PKey::RSA.new(parsed_options[:private_key]).n.num_bits == 1024)
  raise ArgumentError, "Must provide option: #{parsed_options.key(nil).to_s}" if parsed_options.values.include? nil

  return parsed_options
end