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 +10 -0
- data/VERSION +1 -1
- data/lib/tor.rb +5 -2
- data/lib/tor/control.rb +238 -0
- data/lib/tor/version.rb +1 -1
- metadata +5 -4
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.
|
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 :
|
19
|
-
autoload :
|
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.
|
data/lib/tor/control.rb
ADDED
@@ -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
|
data/lib/tor/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
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-
|
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://
|
67
|
+
homepage: http://cypherpunk.rubyforge.org/tor/
|
67
68
|
licenses:
|
68
69
|
- Public Domain
|
69
70
|
post_install_message:
|