tor 0.1.1 → 0.1.2
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 +20 -1
- data/VERSION +1 -1
- data/lib/tor.rb +23 -0
- data/lib/tor/config.rb +103 -0
- data/lib/tor/control.rb +45 -7
- data/lib/tor/version.rb +1 -1
- metadata +3 -2
data/README
CHANGED
@@ -10,10 +10,13 @@ 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 parsing Tor configuration files and looking up the values of
|
14
|
+
particular options.
|
13
15
|
* Supports querying and controlling a locally-running Tor process using the
|
14
16
|
[Tor Control Protocol (TC)][TC] over a socket connection.
|
15
17
|
* Supports querying the [Tor DNS Exit List (DNSEL)][TorDNSEL] to determine
|
16
18
|
whether a particular host is a Tor exit node or not.
|
19
|
+
* Compatible with Ruby 1.8.7+, Ruby 1.9.x, and JRuby 1.4/1.5.
|
17
20
|
|
18
21
|
Examples
|
19
22
|
--------
|
@@ -26,7 +29,22 @@ Examples
|
|
26
29
|
Tor.available? #=> true
|
27
30
|
Tor.version #=> "0.2.1.25"
|
28
31
|
|
29
|
-
###
|
32
|
+
### Parsing the Tor configuration file (1)
|
33
|
+
|
34
|
+
torrc = Tor::Config.load("/etc/tor/torrc")
|
35
|
+
|
36
|
+
### Parsing the Tor configuration file (2)
|
37
|
+
|
38
|
+
Tor::Config.open("/etc/tor/torrc") do |torrc|
|
39
|
+
puts "Tor SOCKS port: #{torrc['SocksPort']}"
|
40
|
+
puts "Tor control port: #{torrc['ControlPort']}"
|
41
|
+
puts "Tor exit policy:"
|
42
|
+
torrc.each('ExitPolicy') do |key, value|
|
43
|
+
puts " #{value}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
### Communicating with a running Tor process
|
30
48
|
|
31
49
|
Tor::Controller.connect(:port => 9051) do |tor|
|
32
50
|
puts "Tor version: #{tor.version}"
|
@@ -47,6 +65,7 @@ Dependencies
|
|
47
65
|
------------
|
48
66
|
|
49
67
|
* [Ruby](http://ruby-lang.org/) (>= 1.8.7) or (>= 1.8.1 with [Backports][])
|
68
|
+
* [Tor](https://www.torproject.org/download.html.en) (>= 0.2.1)
|
50
69
|
|
51
70
|
Installation
|
52
71
|
------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/lib/tor.rb
CHANGED
@@ -17,10 +17,33 @@ end
|
|
17
17
|
##
|
18
18
|
# @see https://www.torproject.org/
|
19
19
|
module Tor
|
20
|
+
autoload :Config, 'tor/config'
|
20
21
|
autoload :Controller, 'tor/control'
|
21
22
|
autoload :DNSEL, 'tor/dnsel'
|
22
23
|
autoload :VERSION, 'tor/version'
|
23
24
|
|
25
|
+
##
|
26
|
+
# Returns `true` if the Tor process is running locally, `false` otherwise.
|
27
|
+
#
|
28
|
+
# This works by attempting to establish a Tor Control Protocol (TC)
|
29
|
+
# connection to the standard control port 9051 on `localhost`. If Tor
|
30
|
+
# hasn't been configured with the `ControlPort 9051` option, this will
|
31
|
+
# return `false`.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# Tor.running? #=> false
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
37
|
+
# @since 0.1.2
|
38
|
+
def self.running?
|
39
|
+
begin
|
40
|
+
Tor::Controller.new.quit
|
41
|
+
true
|
42
|
+
rescue Errno::ECONNREFUSED
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
24
47
|
##
|
25
48
|
# Returns `true` if Tor is available, `false` otherwise.
|
26
49
|
#
|
data/lib/tor/config.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Tor
|
2
|
+
##
|
3
|
+
# Tor configuration.
|
4
|
+
#
|
5
|
+
# @example Parsing a Tor configuration file (1)
|
6
|
+
# torrc = Tor::Config.load("/etc/tor/torrc")
|
7
|
+
#
|
8
|
+
# @example Parsing a Tor configuration file (2)
|
9
|
+
# Tor::Config.open("/etc/tor/torrc") do |torrc|
|
10
|
+
# puts "Tor SOCKS port: #{torrc['SocksPort']}"
|
11
|
+
# puts "Tor control port: #{torrc['ControlPort']}"
|
12
|
+
# puts "Tor exit policy:"
|
13
|
+
# torrc.each('ExitPolicy') do |key, value|
|
14
|
+
# puts " #{value}"
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @see https://www.torproject.org/tor-manual.html.en
|
19
|
+
# @since 0.1.2
|
20
|
+
class Config
|
21
|
+
CONFDIR = '/etc/tor' unless defined?(CONFDIR)
|
22
|
+
|
23
|
+
##
|
24
|
+
# Opens a Tor configuration file.
|
25
|
+
#
|
26
|
+
# @param [String, #to_s] filename
|
27
|
+
# @param [Hash{Symbol => Object}] options
|
28
|
+
# @yield [config]
|
29
|
+
# @yieldparam [Config] config
|
30
|
+
# @return [Config]
|
31
|
+
def self.open(filename, options = {}, &block)
|
32
|
+
if block_given?
|
33
|
+
block.call(self.load(filename, options))
|
34
|
+
else
|
35
|
+
self.load(filename, options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Loads the configuration options from a Tor configuration file.
|
41
|
+
#
|
42
|
+
# @param [String, #to_s] filename
|
43
|
+
# @param [Hash{Symbol => Object}] options
|
44
|
+
# @return [Config]
|
45
|
+
def self.load(filename, options = {})
|
46
|
+
self.new(options) do |config|
|
47
|
+
File.open(filename.to_s, 'rb') do |file|
|
48
|
+
file.each_line do |line|
|
49
|
+
case line = line.strip.chomp.strip
|
50
|
+
when '' then next # skip empty lines
|
51
|
+
when /^#/ then next # skip comments
|
52
|
+
else line = line.split('#').first.strip
|
53
|
+
end
|
54
|
+
# TODO: support for unquoting and unescaping values
|
55
|
+
config << line.split(/\s+/, 2)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# @param [Hash{Symbol => Object}] options
|
63
|
+
# @yield [config]
|
64
|
+
# @yieldparam [Config] config
|
65
|
+
def initialize(options = {}, &block)
|
66
|
+
@lines, @options = [], options.dup
|
67
|
+
block.call(self) if block_given?
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Appends a new configuration option.
|
72
|
+
#
|
73
|
+
# @param [Array(String, String)]
|
74
|
+
# @return [Config]
|
75
|
+
def <<(kv)
|
76
|
+
@lines << kv
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Looks up the last value of a particular configuration option.
|
82
|
+
#
|
83
|
+
# @param [String, Regexp] key
|
84
|
+
# @return [String]
|
85
|
+
def [](key)
|
86
|
+
values = each(key).map(&:last)
|
87
|
+
values.empty? ? nil : values.last
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Enumerates configuration options.
|
92
|
+
#
|
93
|
+
# @param [String, Regexp] key
|
94
|
+
# @yield [key, value]
|
95
|
+
# @yieldparam [String] key
|
96
|
+
# @yieldparam [String] value
|
97
|
+
# @return [Enumerator]
|
98
|
+
def each(key = nil, &block)
|
99
|
+
return enum_for(:each, key) unless block_given?
|
100
|
+
key ? @lines.find_all { |k, v| key === k }.each(&block) : @lines.each(&block)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/tor/control.rb
CHANGED
@@ -82,9 +82,9 @@ module Tor
|
|
82
82
|
# Returns `true` if the controller connection is active.
|
83
83
|
#
|
84
84
|
# @example
|
85
|
-
# tor.connected?
|
85
|
+
# tor.connected? #=> true
|
86
86
|
# tor.close
|
87
|
-
# tor.connected?
|
87
|
+
# tor.connected? #=> false
|
88
88
|
#
|
89
89
|
# @return [Boolean]
|
90
90
|
def connected?
|
@@ -119,19 +119,57 @@ module Tor
|
|
119
119
|
#
|
120
120
|
# @return [void]
|
121
121
|
def quit
|
122
|
-
|
122
|
+
send_line('QUIT')
|
123
123
|
reply = read_reply
|
124
124
|
close
|
125
125
|
reply
|
126
126
|
end
|
127
127
|
|
128
|
+
##
|
129
|
+
# Returns information about the authentication method required by the
|
130
|
+
# Tor process.
|
131
|
+
#
|
132
|
+
# This command may be used before authenticating.
|
133
|
+
#
|
134
|
+
# @example
|
135
|
+
# C: PROTOCOLINFO
|
136
|
+
# S: 250-PROTOCOLINFO 1
|
137
|
+
# S: 250-AUTH METHODS=NULL
|
138
|
+
# S: 250-VERSION Tor="0.2.1.25"
|
139
|
+
# S: 250 OK
|
140
|
+
#
|
141
|
+
# @example
|
142
|
+
# tor.authentication_method #=> nil
|
143
|
+
# tor.authentication_method #=> :hashedpassword
|
144
|
+
# tor.authentication_method #=> :cookie
|
145
|
+
#
|
146
|
+
# @return [Symbol]
|
147
|
+
# @since 0.1.2
|
148
|
+
def authentication_method
|
149
|
+
@authentication_method ||= begin
|
150
|
+
method = nil
|
151
|
+
send_line('PROTOCOLINFO')
|
152
|
+
loop do
|
153
|
+
# TODO: support for reading multiple authentication methods
|
154
|
+
case reply = read_reply
|
155
|
+
when /^250-AUTH METHODS=(\w*)/
|
156
|
+
method = $1.strip.downcase.to_sym
|
157
|
+
method = method.eql?(:null) ? nil : method
|
158
|
+
when /^250-/ then next
|
159
|
+
when '250 OK' then break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
method
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
128
166
|
##
|
129
167
|
# Returns `true` if the controller connection has been authenticated.
|
130
168
|
#
|
131
169
|
# @example
|
132
|
-
# tor.authenticated?
|
170
|
+
# tor.authenticated? #=> false
|
133
171
|
# tor.authenticate
|
134
|
-
# tor.authenticated?
|
172
|
+
# tor.authenticated? #=> true
|
135
173
|
#
|
136
174
|
# @return [Boolean]
|
137
175
|
def authenticated?
|
@@ -169,7 +207,7 @@ module Tor
|
|
169
207
|
# S: 250 OK
|
170
208
|
#
|
171
209
|
# @example
|
172
|
-
# tor.version
|
210
|
+
# tor.version #=> "0.2.1.25"
|
173
211
|
#
|
174
212
|
# @return [String]
|
175
213
|
def version
|
@@ -188,7 +226,7 @@ module Tor
|
|
188
226
|
# S: 250 OK
|
189
227
|
#
|
190
228
|
# @example
|
191
|
-
# tor.config_file
|
229
|
+
# tor.config_file #=> #<Pathname:/opt/local/etc/tor/torrc>
|
192
230
|
#
|
193
231
|
# @return [Pathname]
|
194
232
|
def config_file
|
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
|
+
- 2
|
9
|
+
version: 0.1.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Arto Bendiken
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- README
|
60
60
|
- UNLICENSE
|
61
61
|
- VERSION
|
62
|
+
- lib/tor/config.rb
|
62
63
|
- lib/tor/control.rb
|
63
64
|
- lib/tor/dnsel.rb
|
64
65
|
- lib/tor/version.rb
|