tor 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -10,6 +10,8 @@ Features
10
10
 
11
11
  * Supports checking whether Tor is installed in the user's current `PATH`,
12
12
  and if it is, returning the version number.
13
+ * Supports querying and controlling a locally-running Tor process using the
14
+ [Tor Control Protocol (TC)][TC] over a socket connection.
13
15
  * Supports querying the [Tor DNS Exit List (DNSEL)][TorDNSEL] to determine
14
16
  whether a particular host is a Tor exit node or not.
15
17
 
@@ -24,6 +26,13 @@ Examples
24
26
  Tor.available? #=> true
25
27
  Tor.version #=> "0.2.1.25"
26
28
 
29
+ ### Obtaining information about a running Tor process
30
+
31
+ Tor::Controller.connect(:port => 9051) do |tor|
32
+ puts "Tor version: #{tor.version}"
33
+ puts "Tor config file: #{tor.config_file}"
34
+ end
35
+
27
36
  ### Checking whether a particular host is a Tor exit node
28
37
 
29
38
  Tor::DNSEL.include?("208.75.57.100") #=> true
@@ -73,5 +82,6 @@ information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
73
82
 
74
83
  [Tor]: https://www.torproject.org/
75
84
  [TorDNSEL]: https://www.torproject.org/tordnsel/
85
+ [TC]: http://gitweb.torproject.org/tor.git?a=blob_plain;hb=HEAD;f=doc/spec/control-spec.txt
76
86
  [OR]: http://en.wikipedia.org/wiki/Onion_routing
77
87
  [Backports]: http://rubygems.org/gems/backports
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/lib/tor.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  if RUBY_VERSION < '1.8.7'
2
4
  # @see http://rubygems.org/gems/backports
3
5
  begin
@@ -15,8 +17,9 @@ end
15
17
  ##
16
18
  # @see https://www.torproject.org/
17
19
  module Tor
18
- autoload :DNSEL, 'tor/dnsel'
19
- autoload :VERSION, 'tor/version'
20
+ autoload :Controller, 'tor/control'
21
+ autoload :DNSEL, 'tor/dnsel'
22
+ autoload :VERSION, 'tor/version'
20
23
 
21
24
  ##
22
25
  # Returns `true` if Tor is available, `false` otherwise.
@@ -0,0 +1,238 @@
1
+ require 'socket' unless defined?(Socket)
2
+
3
+ module Tor
4
+ ##
5
+ # Tor Control Protocol (TC) client.
6
+ #
7
+ # The Tor control protocol is used by other programs (such as frontend
8
+ # user interfaces) to communicate with a locally running Tor process. It
9
+ # is not part of the Tor onion routing protocol.
10
+ #
11
+ # @example Establishing a controller connection (1)
12
+ # tor = Tor::Controller.new
13
+ #
14
+ # @example Establishing a controller connection (2)
15
+ # tor = Tor::Controller.new(:host => '127.0.0.1', :port => 9051)
16
+ #
17
+ # @example Authenticating the controller connection
18
+ # tor.authenticate
19
+ #
20
+ # @example Obtaining information about the Tor process
21
+ # tor.version #=> "0.2.1.25"
22
+ # tor.config_file #=> #<Pathname:/opt/local/etc/tor/torrc>
23
+ #
24
+ # @see http://gitweb.torproject.org/tor.git?a=blob_plain;hb=HEAD;f=doc/spec/control-spec.txt
25
+ # @see http://www.thesprawl.org/memdump/?entry=8
26
+ # @since 0.1.1
27
+ class Controller
28
+ PROTOCOL_VERSION = 1
29
+
30
+ ##
31
+ # @param [Hash{Symbol => Object}] options
32
+ # @option options [String, #to_s] :host ("127.0.0.1")
33
+ # @option options [Integer, #to_i] :port (9051)
34
+ # @option options [String, #to_s] :cookie (nil)
35
+ # @option options [Integer, #to_i] :version (PROTOCOL_VERSION)
36
+ def self.connect(options = {}, &block)
37
+ if block_given?
38
+ result = block.call(tor = self.new(options))
39
+ tor.quit
40
+ result
41
+ else
42
+ self.new(options)
43
+ end
44
+ end
45
+
46
+ ##
47
+ # @param [Hash{Symbol => Object}] options
48
+ # @option options [String, #to_s] :host ("127.0.0.1")
49
+ # @option options [Integer, #to_i] :port (9051)
50
+ # @option options [String, #to_s] :cookie (nil)
51
+ # @option options [Integer, #to_i] :version (PROTOCOL_VERSION)
52
+ def initialize(options = {}, &block)
53
+ @options = options.dup
54
+ @host = (@options.delete(:host) || '127.0.0.1').to_s
55
+ @port = (@options.delete(:port) || 9051).to_i
56
+ @version = (@options.delete(:version) || PROTOCOL_VERSION).to_i
57
+ connect
58
+ if block_given?
59
+ block.call(self)
60
+ quit
61
+ end
62
+ end
63
+
64
+ attr_reader :host, :port
65
+
66
+ ##
67
+ # Establishes the socket connection to the Tor process.
68
+ #
69
+ # @example
70
+ # tor.close
71
+ # tor.connect
72
+ #
73
+ # @return [void]
74
+ def connect
75
+ close
76
+ @socket = TCPSocket.new(@host, @port)
77
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
78
+ self
79
+ end
80
+
81
+ ##
82
+ # Returns `true` if the controller connection is active.
83
+ #
84
+ # @example
85
+ # tor.connected? #=> true
86
+ # tor.close
87
+ # tor.connected? #=> false
88
+ #
89
+ # @return [Boolean]
90
+ def connected?
91
+ !!@socket
92
+ end
93
+
94
+ ##
95
+ # Closes the socket connection to the Tor process.
96
+ #
97
+ # @example
98
+ # tor.close
99
+ #
100
+ # @return [void]
101
+ def close
102
+ @socket.close if @socket
103
+ @socket = nil
104
+ self
105
+ end
106
+
107
+ ##
108
+ # Tells the Tor process to hang up on this controller connection.
109
+ #
110
+ # This command can be used before authenticating.
111
+ #
112
+ # @example
113
+ # C: QUIT
114
+ # S: 250 closing connection
115
+ # ^D
116
+ #
117
+ # @example
118
+ # tor.quit
119
+ #
120
+ # @return [void]
121
+ def quit
122
+ send_command(:quit)
123
+ reply = read_reply
124
+ close
125
+ reply
126
+ end
127
+
128
+ ##
129
+ # Returns `true` if the controller connection has been authenticated.
130
+ #
131
+ # @example
132
+ # tor.authenticated? #=> false
133
+ # tor.authenticate
134
+ # tor.authenticated? #=> true
135
+ #
136
+ # @return [Boolean]
137
+ def authenticated?
138
+ @authenticated || false
139
+ end
140
+
141
+ ##
142
+ # Authenticates the controller connection.
143
+ #
144
+ # @example
145
+ # C: AUTHENTICATE
146
+ # S: 250 OK
147
+ #
148
+ # @example
149
+ # tor.authenticate
150
+ #
151
+ # @return [void]
152
+ # @raise [AuthenticationError] if authentication failed
153
+ def authenticate(cookie = nil)
154
+ cookie ||= @options[:cookie]
155
+ send(:send_line, cookie ? "AUTHENTICATE #{cookie}" : "AUTHENTICATE")
156
+ case reply = read_reply
157
+ when '250 OK' then @authenticated = true
158
+ else raise AuthenticationError.new(reply)
159
+ end
160
+ self
161
+ end
162
+
163
+ ##
164
+ # Returns the version number of the Tor process.
165
+ #
166
+ # @example
167
+ # C: GETINFO version
168
+ # S: 250-version=0.2.1.25
169
+ # S: 250 OK
170
+ #
171
+ # @example
172
+ # tor.version #=> "0.2.1.25"
173
+ #
174
+ # @return [String]
175
+ def version
176
+ send_command(:getinfo, 'version')
177
+ reply = read_reply.split('=').last
178
+ read_reply # skip "250 OK"
179
+ reply
180
+ end
181
+
182
+ ##
183
+ # Returns the path to the Tor configuration file.
184
+ #
185
+ # @example
186
+ # C: GETINFO config-file
187
+ # S: 250-config-file=/opt/local/etc/tor/torrc
188
+ # S: 250 OK
189
+ #
190
+ # @example
191
+ # tor.config_file #=> #<Pathname:/opt/local/etc/tor/torrc>
192
+ #
193
+ # @return [Pathname]
194
+ def config_file
195
+ send_command(:getinfo, 'config-file')
196
+ reply = read_reply.split('=').last
197
+ read_reply # skip "250 OK"
198
+ Pathname(reply)
199
+ end
200
+
201
+ protected
202
+
203
+ ##
204
+ # Sends a command line over the socket.
205
+ #
206
+ # @param [Symbol, #to_s] command
207
+ # @param [Array<String>] args
208
+ # @return [void]
209
+ def send_command(command, *args)
210
+ authenticate unless authenticated?
211
+ send_line(["#{command.to_s.upcase}", *args].join(' '))
212
+ end
213
+
214
+ ##
215
+ # Sends a text line over the socket.
216
+ #
217
+ # @param [String, #to_s] line
218
+ # @return [void]
219
+ def send_line(line)
220
+ @socket.write(line.to_s + "\r\n")
221
+ @socket.flush
222
+ end
223
+
224
+ ##
225
+ # Reads a reply line from the socket.
226
+ #
227
+ # @return [String]
228
+ def read_reply
229
+ @socket.readline.chomp
230
+ end
231
+
232
+ ##
233
+ # Used to signal an authentication error.
234
+ #
235
+ # @see Tor::Controller#authenticate
236
+ class AuthenticationError < StandardError; end
237
+ end
238
+ end
@@ -2,7 +2,7 @@ module Tor
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
  EXTRA = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 0
9
- version: 0.1.0
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Arto Bendiken
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-01 00:00:00 +02:00
17
+ date: 2010-08-02 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -59,11 +59,12 @@ files:
59
59
  - README
60
60
  - UNLICENSE
61
61
  - VERSION
62
+ - lib/tor/control.rb
62
63
  - lib/tor/dnsel.rb
63
64
  - lib/tor/version.rb
64
65
  - lib/tor.rb
65
66
  has_rdoc: false
66
- homepage: http://github.com/bendiken/tor-ruby
67
+ homepage: http://cypherpunk.rubyforge.org/tor/
67
68
  licenses:
68
69
  - Public Domain
69
70
  post_install_message: