tor2 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f03a9d3b85ee79e8662979fb5dd55d53a14e777e
4
+ data.tar.gz: 6d3864c849f7c13be6ed17b1bd326d2c4597eac8
5
+ SHA512:
6
+ metadata.gz: cc744f5aa9b4b0ba45e445df1bb592e24d02f0cf16e6fb84f6b00bd8116b48cba0752fb1e68bae038d5b172aa52e50005912bae664ad27668f0ca11259bd889f
7
+ data.tar.gz: b57b310df5a8d18980ab29dd87d5042b1fad8b50a23e7a2a670c7bcd37a54917c56b123b6d2b1487ba77f9e0be9e886a39220d1e705e9532bdffef46b4e5fdd6
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ * Arto Bendiken <arto.bendiken@gmail.com>
data/CREDITS ADDED
File without changes
@@ -0,0 +1,108 @@
1
+ Tor.rb: Onion Routing for Ruby (tor2)
2
+ =====================================
3
+
4
+ This is a Ruby library for interacting with the [Tor][] anonymity network.
5
+
6
+ **/ ! \ This repository were created to keep an updated version of this lib on the gem store. I am not the author or the maintainer even if PR are welcomed. The original project can be found at <https://github.com/dryruby/tor.rb> (previously <http://github.com/bendiken/tor-ruby>).**
7
+
8
+ The name of this alternative gem is `tor2` to avoid confusion with the original.
9
+
10
+ Features
11
+ --------
12
+
13
+ * Supports checking whether Tor is installed in the user's current `PATH`,
14
+ and if it is, returning the version number.
15
+ * Supports parsing Tor configuration files and looking up the values of
16
+ particular options.
17
+ * Supports querying and controlling a locally-running Tor process using the
18
+ [Tor Control Protocol (TC)][TC] over a socket connection.
19
+ * Supports querying the [Tor DNS Exit List (DNSEL)][TorDNSEL] to determine
20
+ whether a particular host is a Tor exit node or not.
21
+ * Compatible with Ruby 1.8.7+, Ruby 1.9.x, and JRuby 1.4/1.5.
22
+
23
+ Examples
24
+ --------
25
+
26
+ require 'rubygems'
27
+ require 'tor2'
28
+
29
+ ### Checking whether Tor is installed and which version it is
30
+
31
+ Tor.available? #=> true
32
+ Tor.version #=> "0.2.1.25"
33
+
34
+ ### Parsing the Tor configuration file (1)
35
+
36
+ torrc = Tor::Config.load("/etc/tor/torrc")
37
+
38
+ ### Parsing the Tor configuration file (2)
39
+
40
+ Tor::Config.open("/etc/tor/torrc") do |torrc|
41
+ puts "Tor SOCKS port: #{torrc['SocksPort']}"
42
+ puts "Tor control port: #{torrc['ControlPort']}"
43
+ puts "Tor exit policy:"
44
+ torrc.each('ExitPolicy') do |key, value|
45
+ puts " #{value}"
46
+ end
47
+ end
48
+
49
+ ### Communicating with a running Tor process
50
+
51
+ Tor::Controller.connect(:port => 9051) do |tor|
52
+ puts "Tor version: #{tor.version}"
53
+ puts "Tor config file: #{tor.config_file}"
54
+ end
55
+
56
+ ### Checking whether a particular host is a Tor exit node
57
+
58
+ Tor::DNSEL.include?("208.75.57.100") #=> true
59
+ Tor::DNSEL.include?("1.2.3.4") #=> false
60
+
61
+ Documentation
62
+ -------------
63
+
64
+ * <http://cypherpunk.rubyforge.org/tor/>
65
+
66
+ Dependencies
67
+ ------------
68
+
69
+ * [Ruby](http://ruby-lang.org/) (>= 1.8.7) or (>= 1.8.1 with [Backports][])
70
+ * [Tor](https://www.torproject.org/download.html.en) (>= 0.2.1)
71
+
72
+ Installation
73
+ ------------
74
+
75
+ The recommended installation method is via [RubyGems](http://rubygems.org/).
76
+ To install the latest official release of Tor.rb, do:
77
+
78
+ % [sudo] gem install tor2 # Ruby 1.8.7+ or 1.9.x
79
+ % [sudo] gem install backports tor2 # Ruby 1.8.1+
80
+
81
+ Download
82
+ --------
83
+
84
+ To get a local working copy of the development repository, do:
85
+
86
+ % git clone git://github.com/alexmili/tor.rb.git
87
+
88
+ Alternatively, you can download the latest development version as a tarball
89
+ as follows:
90
+
91
+ % wget http://github.com/alexmili/tor.rb/tarball/master
92
+
93
+ Author
94
+ ------
95
+
96
+ * [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
97
+
98
+ License
99
+ -------
100
+
101
+ Tor.rb is free and unencumbered public domain software. For more
102
+ information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
103
+
104
+ [Tor]: https://www.torproject.org/
105
+ [TorDNSEL]: https://www.torproject.org/tordnsel/
106
+ [TC]: http://gitweb.torproject.org/tor.git?a=blob_plain;hb=HEAD;f=doc/spec/control-spec.txt
107
+ [OR]: http://en.wikipedia.org/wiki/Onion_routing
108
+ [Backports]: http://rubygems.org/gems/backports
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -0,0 +1,89 @@
1
+ require 'pathname'
2
+
3
+ if RUBY_VERSION < '1.8.7'
4
+ # @see http://rubygems.org/gems/backports
5
+ begin
6
+ require 'backports/1.8.7'
7
+ rescue LoadError
8
+ begin
9
+ require 'rubygems'
10
+ require 'backports/1.8.7'
11
+ rescue LoadError
12
+ abort "Tor.rb requires Ruby 1.8.7 or the Backports gem (hint: `gem install backports')."
13
+ end
14
+ end
15
+ end
16
+
17
+ ##
18
+ # @see https://www.torproject.org/
19
+ module Tor
20
+ require_relative 'tor/config'
21
+ require_relative 'tor/control'
22
+ require_relative 'tor/dnsel'
23
+ require_relative 'tor/version'
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
+
47
+ ##
48
+ # Returns `true` if Tor is available, `false` otherwise.
49
+ #
50
+ # @example
51
+ # Tor.available? #=> true
52
+ #
53
+ # @return [Boolean]
54
+ def self.available?
55
+ !!program_path
56
+ end
57
+
58
+ ##
59
+ # Returns the Tor version number, or `nil` if Tor is not available.
60
+ #
61
+ # @example
62
+ # Tor.version #=> "0.2.1.25"
63
+ #
64
+ # @return [String]
65
+ def self.version
66
+ if available? && `#{program_path} --version` =~ /Tor v(\d+)\.(\d+)\.(\d+)\.(\d+)/
67
+ [$1, $2, $3, $4].join('.')
68
+ elsif available? && `#{program_path} --version` =~ /Tor version (\d+)\.(\d+)\.(\d+)\.(\d+)/
69
+ [$1, $2, $3, $4].join('.')
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Returns the path to the `tor` executable, or `nil` if the program could
75
+ # not be found in the user's current `PATH` environment.
76
+ #
77
+ # @example
78
+ # Tor.program_path #=> "/opt/local/bin/tor"
79
+ #
80
+ # @param [String, #to_s] program_name
81
+ # @return [String]
82
+ def self.program_path(program_name = :tor)
83
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
84
+ program_path = File.join(path, program_name.to_s)
85
+ return program_path if File.executable?(program_path)
86
+ end
87
+ return nil
88
+ end
89
+ end
@@ -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
@@ -0,0 +1,310 @@
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_line('QUIT')
123
+ reply = read_reply
124
+ close
125
+ reply
126
+ end
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
+
166
+ ##
167
+ # Returns `true` if the controller connection has been authenticated.
168
+ #
169
+ # @example
170
+ # tor.authenticated? #=> false
171
+ # tor.authenticate
172
+ # tor.authenticated? #=> true
173
+ #
174
+ # @return [Boolean]
175
+ def authenticated?
176
+ @authenticated || false
177
+ end
178
+
179
+ ##
180
+ # Authenticates the controller connection.
181
+ #
182
+ # @example
183
+ # C: AUTHENTICATE
184
+ # S: 250 OK
185
+ #
186
+ # @example
187
+ # tor.authenticate
188
+ #
189
+ # @return [void]
190
+ # @raise [AuthenticationError] if authentication failed
191
+ def authenticate(cookie = nil)
192
+ cookie ||= @options[:cookie]
193
+ send(:send_line, cookie ? "AUTHENTICATE \"#{cookie}\"" : "AUTHENTICATE")
194
+ case reply = read_reply
195
+ when '250 OK' then @authenticated = true
196
+ else raise AuthenticationError.new(reply)
197
+ end
198
+ self
199
+ end
200
+
201
+ ##
202
+ # Returns the version number of the Tor process.
203
+ #
204
+ # @example
205
+ # C: GETINFO version
206
+ # S: 250-version=0.2.1.25
207
+ # S: 250 OK
208
+ #
209
+ # @example
210
+ # tor.version #=> "0.2.1.25"
211
+ #
212
+ # @return [String]
213
+ def version
214
+ send_command(:getinfo, 'version')
215
+ reply = read_reply.split('=').last
216
+ read_reply # skip "250 OK"
217
+ reply
218
+ end
219
+
220
+ ##
221
+ # Returns the path to the Tor configuration file.
222
+ #
223
+ # @example
224
+ # C: GETINFO config-file
225
+ # S: 250-config-file=/opt/local/etc/tor/torrc
226
+ # S: 250 OK
227
+ #
228
+ # @example
229
+ # tor.config_file #=> #<Pathname:/opt/local/etc/tor/torrc>
230
+ #
231
+ # @return [Pathname]
232
+ def config_file
233
+ send_command(:getinfo, 'config-file')
234
+ reply = read_reply.split('=').last
235
+ read_reply # skip "250 OK"
236
+ Pathname(reply)
237
+ end
238
+
239
+ ##
240
+ # Returns the current (in-memory) Tor configuration.
241
+ # Response is terminated with a "."
242
+ #
243
+ # @example
244
+ # C: GETINFO config-text
245
+ # S: 250+config-text=
246
+ # S: ControlPort 9051
247
+ # S: RunAsDaemon 1
248
+ # S: .
249
+ def config_text
250
+ send_command(:getinfo, 'config-text')
251
+ reply = ""
252
+ read_reply # skip "250+config-text="
253
+ while line = read_reply
254
+ break unless line != "."
255
+ reply.concat(line + "\n")
256
+ end
257
+ read_reply # skip "250 OK"
258
+ return reply
259
+ end
260
+
261
+ ##
262
+ # Send a signal to the server
263
+ #
264
+ # @example
265
+ # tor.signal("newnym")
266
+ #
267
+ # @return [String]
268
+ def signal(name)
269
+ send_command(:signal, name)
270
+ read_reply
271
+ end
272
+
273
+ protected
274
+
275
+ ##
276
+ # Sends a command line over the socket.
277
+ #
278
+ # @param [Symbol, #to_s] command
279
+ # @param [Array<String>] args
280
+ # @return [void]
281
+ def send_command(command, *args)
282
+ authenticate unless authenticated?
283
+ send_line(["#{command.to_s.upcase}", *args].join(' '))
284
+ end
285
+
286
+ ##
287
+ # Sends a text line over the socket.
288
+ #
289
+ # @param [String, #to_s] line
290
+ # @return [void]
291
+ def send_line(line)
292
+ @socket.write(line.to_s + "\r\n")
293
+ @socket.flush
294
+ end
295
+
296
+ ##
297
+ # Reads a reply line from the socket.
298
+ #
299
+ # @return [String]
300
+ def read_reply
301
+ @socket.readline.chomp
302
+ end
303
+
304
+ ##
305
+ # Used to signal an authentication error.
306
+ #
307
+ # @see Tor::Controller#authenticate
308
+ class AuthenticationError < StandardError; end
309
+ end
310
+ end
@@ -0,0 +1,148 @@
1
+ require 'resolv' unless defined?(Resolv)
2
+
3
+ module Tor
4
+ ##
5
+ # Tor DNS Exit List (DNSEL) client.
6
+ #
7
+ # Unless the target IP address and port are explicitly specified, the
8
+ # query will be performed using a target IP address of "8.8.8.8" and a
9
+ # target port of 53. These correspond to the DNS protocol port on one of
10
+ # the [Google Public DNS](http://code.google.com/speed/public-dns/)
11
+ # servers, and they are guaranteed to be reachable from Tor's default exit
12
+ # policy.
13
+ #
14
+ # @example Checking source IP addresses
15
+ # Tor::DNSEL.include?("208.75.57.100") #=> true
16
+ # Tor::DNSEL.include?("1.2.3.4") #=> false
17
+ #
18
+ # @example Checking source hostnames
19
+ # Tor::DNSEL.include?("ennui.lostinthenoise.net") #=> true
20
+ # Tor::DNSEL.include?("myhost.example.org") #=> false
21
+ #
22
+ # @example Specifying an explicit target port
23
+ # Tor::DNSEL.include?("208.75.57.100", :port => 80) #=> true
24
+ # Tor::DNSEL.include?("208.75.57.100", :port => 25) #=> false
25
+ #
26
+ # @example Specifying an explicit target IP address and port
27
+ # Tor::DNSEL.include?(source_addr, :addr => target_addr, :port => target_port)
28
+ # Tor::DNSEL.include?("208.75.57.100", :addr => myip, :port => myport)
29
+ #
30
+ # @example Using from a Rack application
31
+ # Tor::DNSEL.include?(env['REMOTE_ADDR'] || env['REMOTE_HOST'], {
32
+ # :addr => env['SERVER_NAME'],
33
+ # :port => env['SERVER_PORT'],
34
+ # })
35
+ #
36
+ # @see https://www.torproject.org/tordnsel/
37
+ # @see https://trac.torproject.org/projects/tor/wiki/TheOnionRouter/TorDNSExitList
38
+ # @see http://gitweb.torproject.org/tor.git?a=blob_plain;hb=HEAD;f=doc/contrib/torel-design.txt
39
+ module DNSEL
40
+ RESOLVER = Resolv::DefaultResolver unless defined?(RESOLVER)
41
+ TARGET_ADDR = '8.8.8.8'.freeze unless defined?(TARGET_ADDR) # Google Public DNS
42
+ TARGET_PORT = 53 unless defined?(TARGET_PORT) # DNS
43
+ DNS_SUFFIX = 'ip-port.exitlist.torproject.org'.freeze
44
+
45
+ ##
46
+ # Returns `true` if the Tor DNSEL includes `host`, `false` otherwise.
47
+ #
48
+ # If the DNS server is unreachable or the DNS query times out, returns
49
+ # `nil` to indicate that we don't have a definitive answer one way or
50
+ # another.
51
+ #
52
+ # @example
53
+ # Tor::DNSEL.include?("208.75.57.100") #=> true
54
+ # Tor::DNSEL.include?("1.2.3.4") #=> false
55
+ #
56
+ # @param [String, #to_s] host
57
+ # @param [Hash{Symbol => Object}] options
58
+ # @option options [String, #to_s] :addr ("8.8.8.8")
59
+ # @option options [Integer, #to_i] :port (53)
60
+ # @return [Boolean]
61
+ def self.include?(host, options = {})
62
+ begin
63
+ query(host, options) == '127.0.0.2'
64
+ rescue Resolv::ResolvError # NXDOMAIN
65
+ false
66
+ rescue Resolv::ResolvTimeout
67
+ nil
68
+ rescue Errno::EHOSTUNREACH
69
+ nil
70
+ rescue Errno::EADDRNOTAVAIL
71
+ nil
72
+ end
73
+ end
74
+
75
+ ##
76
+ # Queries the Tor DNSEL for `host`, returning "172.0.0.2" if it is an
77
+ # exit node and raising a `Resolv::ResolvError` if it isn't.
78
+ #
79
+ # @example
80
+ # Tor::DNSEL.query("208.75.57.100") #=> "127.0.0.2"
81
+ # Tor::DNSEL.query("1.2.3.4") #=> Resolv::ResolvError
82
+ #
83
+ # @param [String, #to_s] host
84
+ # @param [Hash{Symbol => Object}] options
85
+ # @option options [String, #to_s] :addr ("8.8.8.8")
86
+ # @option options [Integer, #to_i] :port (53)
87
+ # @return [String]
88
+ # @raise [Resolv::ResolvError] for an NXDOMAIN response
89
+ def self.query(host, options = {})
90
+ getaddress(dnsname(host, options))
91
+ end
92
+
93
+ ##
94
+ # Returns the DNS name used for Tor DNSEL queries of `host`.
95
+ #
96
+ # @example
97
+ # Tor::DNSEL.dnsname("1.2.3.4") #=> "4.3.2.1.53.8.8.8.8.ip-port.exitlist.torproject.org"
98
+ #
99
+ # @param [String, #to_s] host
100
+ # @param [Hash{Symbol => Object}] options
101
+ # @option options [String, #to_s] :addr ("8.8.8.8")
102
+ # @option options [Integer, #to_i] :port (53)
103
+ # @return [String]
104
+ def self.dnsname(host, options = {})
105
+ source_addr = getaddress(host, true)
106
+ target_addr = getaddress(options[:addr] || TARGET_ADDR, true)
107
+ target_port = options[:port] || TARGET_PORT
108
+ [source_addr, target_port, target_addr, DNS_SUFFIX].join('.')
109
+ end
110
+ class << self; alias_method :hostname, :dnsname; end
111
+
112
+ protected
113
+
114
+ ##
115
+ # Resolves `host` into an IPv4 address using Ruby's default resolver.
116
+ #
117
+ # Optionally returns the IPv4 address with its octet order reversed.
118
+ #
119
+ # @example
120
+ # Tor::DNSEL.getaddress("ruby-lang.org") #=> "221.186.184.68"
121
+ # Tor::DNSEL.getaddress("1.2.3.4") #=> "1.2.3.4"
122
+ # Tor::DNSEL.getaddress("1.2.3.4", true) #=> "4.3.2.1"
123
+ #
124
+ # @param [String, #to_s] host
125
+ # @param [Boolean] reversed
126
+ # @return [String]
127
+ def self.getaddress(host, reversed = false)
128
+ host = case host.to_s
129
+ when Resolv::IPv6::Regex
130
+ raise ArgumentError.new("not an IPv4 address: #{host}")
131
+ when Resolv::IPv4::Regex
132
+ host.to_s
133
+ else
134
+ begin
135
+ RESOLVER.each_address(host.to_s) do |addr|
136
+ return addr.to_s if addr.to_s =~ Resolv::IPv4::Regex
137
+ end
138
+ raise Resolv::ResolvError.new("no address for #{host}")
139
+ rescue NoMethodError
140
+ # This is a workaround for Ruby bug #2614:
141
+ # @see http://redmine.ruby-lang.org/issues/show/2614
142
+ raise Resolv::ResolvError.new("no address for #{host}")
143
+ end
144
+ end
145
+ reversed ? host.split('.').reverse.join('.') : host
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,22 @@
1
+ module Tor
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 2
6
+ EXTRA = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
9
+
10
+ ##
11
+ # @return [String]
12
+ def self.to_s() STRING end
13
+
14
+ ##
15
+ # @return [String]
16
+ def self.to_str() STRING end
17
+
18
+ ##
19
+ # @return [Array(Integer, Integer, Integer)]
20
+ def self.to_a() [MAJOR, MINOR, TINY] end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tor2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Arto Bendiken
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.3.0
41
+ description: Tor.rb is a Ruby library for interacting with the Tor anonymity network.
42
+ email: or-talk@seul.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - AUTHORS
48
+ - CREDITS
49
+ - README.md
50
+ - UNLICENSE
51
+ - VERSION
52
+ - lib/tor/config.rb
53
+ - lib/tor/control.rb
54
+ - lib/tor/dnsel.rb
55
+ - lib/tor/version.rb
56
+ - lib/tor.rb
57
+ homepage: https://github.com/AlexMili/tor.rb
58
+ licenses:
59
+ - Public Domain
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.8.1
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements:
76
+ - Tor (>= 0.2.1.25)
77
+ rubyforge_project: cypherpunk
78
+ rubygems_version: 2.0.14.1
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Onion routing for Ruby.
82
+ test_files: []