class Tor::Controller
Tor
Control Protocol (TC) client.
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
Public Class Methods
@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/tor/control.rb, line 36 def self.connect(options = {}, &block) if block_given? result = block.call(tor = self.new(options)) tor.quit result else self.new(options) end end
@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/tor/control.rb, line 52 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
Authenticates the controller connection.
@example
C: AUTHENTICATE S: 250 OK
@example
tor.authenticate
@return [void] @raise [AuthenticationError] if authentication failed
# File lib/tor/control.rb, line 191 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
Returns `true` if the controller connection has been authenticated.
@example
tor.authenticated? #=> false tor.authenticate tor.authenticated? #=> true
@return [Boolean]
# File lib/tor/control.rb, line 175 def authenticated? @authenticated || false end
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/tor/control.rb, line 148 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
Closes the socket connection to the Tor
process.
@example
tor.close
@return [void]
# File lib/tor/control.rb, line 101 def close @socket.close if @socket @socket = nil self end
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/tor/control.rb, line 232 def config_file send_command(:getinfo, 'config-file') reply = read_reply.split('=').last read_reply # skip "250 OK" Pathname(reply) end
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/tor/control.rb, line 249 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
Establishes the socket connection to the Tor
process.
@example
tor.close tor.connect
@return [void]
# File lib/tor/control.rb, line 74 def connect close @socket = TCPSocket.new(@host, @port) @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) self end
Returns `true` if the controller connection is active.
@example
tor.connected? #=> true tor.close tor.connected? #=> false
@return [Boolean]
# File lib/tor/control.rb, line 90 def connected? !!@socket end
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/tor/control.rb, line 121 def quit send_line('QUIT') reply = read_reply close reply end
Send a signal to the server
@example tor.signal(“newnym”)
@return [String]
# File lib/tor/control.rb, line 268 def signal(name) send_command(:signal, name) read_reply end
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/tor/control.rb, line 213 def version send_command(:getinfo, 'version') reply = read_reply.split('=').last read_reply # skip "250 OK" reply end
Protected Instance Methods
Reads a reply line from the socket.
@return [String]
# File lib/tor/control.rb, line 300 def read_reply @socket.readline.chomp end
Sends a command line over the socket.
@param [Symbol, to_s] command @param [Array<String>] args @return [void]
# File lib/tor/control.rb, line 281 def send_command(command, *args) authenticate unless authenticated? send_line(["#{command.to_s.upcase}", *args].join(' ')) end
Sends a text line over the socket.
@param [String, to_s] line @return [void]
# File lib/tor/control.rb, line 291 def send_line(line) @socket.write(line.to_s + "\r\n") @socket.flush end