tor 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: