class Tor::Controller

Tor Control Protocol (TC) client. pulled from tor.rb: github.com/dryruby/tor.rb/blob/master/lib/tor/control.rb because latest version was not pushed to rubygems and needed to bundle into my gem

The Tor control protocol is used by other programs (such as frontend user interfaces) to communicate with a locally running Tor process. It is not part of the Tor onion routing protocol.

@example Establishing a controller connection (1)

tor = Tor::Controller.new

@example Establishing a controller connection (2)

tor = Tor::Controller.new(:host => '127.0.0.1', :port => 9051)

@example Authenticating the controller connection

tor.authenticate

@example Obtaining information about the Tor process

tor.version      #=> "0.2.1.25"
tor.config_file  #=> #<Pathname:/opt/local/etc/tor/torrc>

@see gitweb.torproject.org/tor.git?a=blob_plain;hb=HEAD;f=doc/spec/control-spec.txt @see www.thesprawl.org/memdump/?entry=8 @since 0.1.1

Constants

PROTOCOL_VERSION

Attributes

host[R]
port[R]

Public Class Methods

connect(options = {}, &block) click to toggle source

@param [Hash{Symbol => Object}] options @option options [String, to_s] :host (“127.0.0.1”) @option options [Integer, to_i] :port (9051) @option options [String, to_s] :cookie (nil) @option options [Integer, to_i] :version (PROTOCOL_VERSION)

# File lib/tormanager/control.rb, line 39
def self.connect(options = {}, &block)
  if block_given?
    result = block.call(tor = self.new(options))
    tor.quit
    result
  else
    self.new(options)
  end
end
new(options = {}, &block) click to toggle source

@param [Hash{Symbol => Object}] options @option options [String, to_s] :host (“127.0.0.1”) @option options [Integer, to_i] :port (9051) @option options [String, to_s] :cookie (nil) @option options [Integer, to_i] :version (PROTOCOL_VERSION)

# File lib/tormanager/control.rb, line 55
def initialize(options = {}, &block)
  @options = options.dup
  @host    = (@options.delete(:host)    || '127.0.0.1').to_s
  @port    = (@options.delete(:port)    || 9051).to_i
  @version = (@options.delete(:version) || PROTOCOL_VERSION).to_i
  connect
  if block_given?
    block.call(self)
    quit
  end
end

Public Instance Methods

authenticate(cookie = nil) click to toggle source

Authenticates the controller connection.

@example

C: AUTHENTICATE
S: 250 OK

@example

tor.authenticate

@return [void] @raise [AuthenticationError] if authentication failed

# File lib/tormanager/control.rb, line 194
def authenticate(cookie = nil)
  cookie ||= @options[:cookie]
  send(:send_line, cookie ? "AUTHENTICATE \"#{cookie}\"" : "AUTHENTICATE")
  case reply = read_reply
    when '250 OK' then @authenticated = true
    else raise AuthenticationError.new(reply)
  end
  self
end
authenticated?() click to toggle source

Returns `true` if the controller connection has been authenticated.

@example

tor.authenticated?         #=> false
tor.authenticate
tor.authenticated?         #=> true

@return [Boolean]

# File lib/tormanager/control.rb, line 178
def authenticated?
  @authenticated || false
end
authentication_method() click to toggle source

Returns information about the authentication method required by the Tor process.

This command may be used before authenticating.

@example

C: PROTOCOLINFO
S: 250-PROTOCOLINFO 1
S: 250-AUTH METHODS=NULL
S: 250-VERSION Tor="0.2.1.25"
S: 250 OK

@example

tor.authentication_method  #=> nil
tor.authentication_method  #=> :hashedpassword
tor.authentication_method  #=> :cookie

@return [Symbol] @since 0.1.2

# File lib/tormanager/control.rb, line 151
def authentication_method
  @authentication_method ||= begin
    method = nil
    send_line('PROTOCOLINFO')
    loop do
      # TODO: support for reading multiple authentication methods
      case reply = read_reply
        when /^250-AUTH METHODS=(\w*)/
          method = $1.strip.downcase.to_sym
          method = method.eql?(:null) ? nil : method
        when /^250-/  then next
        when '250 OK' then break
      end
    end
    method
  end
end
close() click to toggle source

Closes the socket connection to the Tor process.

@example

tor.close

@return [void]

# File lib/tormanager/control.rb, line 104
def close
  @socket.close if @socket
  @socket = nil
  self
end
config_file() click to toggle source

Returns the path to the Tor configuration file.

@example

C: GETINFO config-file
S: 250-config-file=/opt/local/etc/tor/torrc
S: 250 OK

@example

tor.config_file            #=> #<Pathname:/opt/local/etc/tor/torrc>

@return [Pathname]

# File lib/tormanager/control.rb, line 235
def config_file
  send_command(:getinfo, 'config-file')
  reply = read_reply.split('=').last
  read_reply # skip "250 OK"
  Pathname(reply)
end
config_text() click to toggle source

Returns the current (in-memory) Tor configuration. Response is terminated with a “.”

@example

C: GETINFO config-text
S: 250+config-text=
S: ControlPort 9051
S: RunAsDaemon 1
S: .
# File lib/tormanager/control.rb, line 252
def config_text
  send_command(:getinfo, 'config-text')
  reply = ""
  read_reply # skip "250+config-text="
  while line = read_reply
    break unless line != "."
    reply.concat(line + "\n")
  end
  read_reply # skip "250 OK"
  return reply
end
connect() click to toggle source

Establishes the socket connection to the Tor process.

@example

tor.close
tor.connect

@return [void]

# File lib/tormanager/control.rb, line 77
def connect
  close
  @socket = TCPSocket.new(@host, @port)
  @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
  self
end
connected?() click to toggle source

Returns `true` if the controller connection is active.

@example

tor.connected?             #=> true
tor.close
tor.connected?             #=> false

@return [Boolean]

# File lib/tormanager/control.rb, line 93
def connected?
  !!@socket
end
quit() click to toggle source

Tells the Tor process to hang up on this controller connection.

This command can be used before authenticating.

@example

C: QUIT
S: 250 closing connection
^D

@example

tor.quit

@return [void]

# File lib/tormanager/control.rb, line 124
def quit
  send_line('QUIT')
  reply = read_reply
  close
  reply
end
signal(name) click to toggle source

Send a signal to the server

@example tor.signal(“newnym”)

@return [String]

# File lib/tormanager/control.rb, line 271
def signal(name)
  send_command(:signal, name)
  read_reply
end
version() click to toggle source

Returns the version number of the Tor process.

@example

C: GETINFO version
S: 250-version=0.2.1.25
S: 250 OK

@example

tor.version                #=> "0.2.1.25"

@return [String]

# File lib/tormanager/control.rb, line 216
def version
  send_command(:getinfo, 'version')
  reply = read_reply.split('=').last
  read_reply # skip "250 OK"
  reply
end

Protected Instance Methods

read_reply() click to toggle source

Reads a reply line from the socket.

@return [String]

# File lib/tormanager/control.rb, line 303
def read_reply
  @socket.readline.chomp
end
send_command(command, *args) click to toggle source

Sends a command line over the socket.

@param [Symbol, to_s] command @param [Array<String>] args @return [void]

# File lib/tormanager/control.rb, line 284
def send_command(command, *args)
  authenticate unless authenticated?
  send_line(["#{command.to_s.upcase}", *args].join(' '))
end
send_line(line) click to toggle source

Sends a text line over the socket.

@param [String, to_s] line @return [void]

# File lib/tormanager/control.rb, line 294
def send_line(line)
  @socket.write(line.to_s + "\r\n")
  @socket.flush
end