onion 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,22 +1,5 @@
1
- = Onion
2
-
3
- == Introduction
4
-
5
- Onion is a library for interacting with Tor. Onion is in an alpha state and is
6
- made availible for early adopters and contributors only.
7
-
8
- == Functionality
9
-
10
- * Parse router status entries per the directory protocol version 2.0.
11
-
12
- == Authors
13
-
14
- Onion was written by Tim Sally <poet@stack.io>. Please send bug reports and
15
- suggestions to the author.
16
-
17
- == License
18
-
19
- (The MIT License)
1
+ LICENSE.txt
2
+ ===========
20
3
 
21
4
  Copyright (c) 2010 Tim Sally
22
5
 
@@ -0,0 +1,34 @@
1
+ README.txt
2
+ ==========
3
+
4
+ Introduction
5
+ ------------
6
+
7
+ Onion is a library for interacting with Tor. Onion is in an alpha state and is
8
+ made available for early adopters and contributors only.
9
+
10
+ Basic Concepts
11
+ --------------
12
+
13
+ Elements are pieces of the Tor network. Routers, circuits, and streams are
14
+ examples of elements. You don't care about how the library gets these, you
15
+ just want to be able to interact with them. Methods in the Control Client
16
+ return elements.
17
+
18
+ Parsers take information received from Tor and turn the information into
19
+ elements. Parsers are used internally by the Control Client to create elements
20
+ to return to the user.
21
+
22
+ The Control Client is what you can use in scripts to interact with Tor.
23
+
24
+ Authors
25
+ -------
26
+
27
+ Onion was written by Tim Sally <poet@stack.io>. Please send bug reports and
28
+ suggestions to the author.
29
+
30
+ License
31
+ -------
32
+
33
+ Copyright (c) 2010 Tim Sally and provided under the MIT License. Please see
34
+ LICENSE.txt for details.
@@ -1,8 +1,8 @@
1
1
  module Onion
2
-
2
+ require 'onion/control_client'
3
3
  require 'onion/elements'
4
4
 
5
- parsers = %w[ router_lists ]
5
+ parsers = %w[ common circuit_lists router_statuses stream_lists ]
6
6
  parsers.each do |parser|
7
7
  begin
8
8
  # If there is a pre-compiled parser, load it.
@@ -0,0 +1,87 @@
1
+ require 'logger'
2
+ require 'socket'
3
+
4
+ module Onion
5
+ # Onion::ControlClient is a client to the Tor Control Protocol.
6
+ class ControlClient
7
+ attr_reader :host, :port
8
+
9
+ def initialize(host="127.0.0.1", port=9051, log_level=Logger::ERROR)
10
+ @log = Logger.new(STDOUT)
11
+ @log.level = log_level
12
+
13
+ @host = host # Host of the Tor process.
14
+ @port = port # Port accepting the Tor Control Protocol.
15
+ @sock = TCPSocket.new(host, port)
16
+
17
+ @log.info("[TOR CONTROL CLIENT ] Connected to #{host}:#{port}.")
18
+ end
19
+
20
+ def send(text)
21
+ @sock << "#{text}\r\n"
22
+ @log.debug("[TOR CONTROL CLIENT] #{text}")
23
+ end
24
+
25
+ def authenticate(pass="\"\"")
26
+ send("authenticate #{pass}")
27
+ @log.debug("[TOR CONTROL CLIENT] #{@sock.gets}")
28
+ end
29
+
30
+ def extend_circuit(specs, id=0)
31
+ send("extendcircuit #{id} #{specs.join(",")}")
32
+ response = @sock.gets
33
+ if response.split[1] == "EXTENDED"
34
+ @log.debug("[TOR CONTROL CLIENT] #{response}")
35
+ return response.split[2]
36
+ else
37
+ @log.warn("[TOR CONTROL CLIENT] Circuit (#{specs.join(",")}) was not extended.")
38
+ @log.warn("[TOR CONTROL CLIENT] #{response}.")
39
+
40
+ return -1
41
+ end
42
+ end
43
+
44
+ def attach_stream(stream_id, circuit_id)
45
+ send("attachstream #{stream_id} #{circuit_id}")
46
+ response = @sock.gets
47
+ if response.split[1] == "OK"
48
+ @log.info("[TOR CONTROL CLIENT] #{response}")
49
+ else
50
+ @log.warn("[TOR CONTROL CLIENT] Stream #{stream_id} was not attached to circuit #{circuit_id}.")
51
+ @log.warn("[TOR CONTROL CLIENT] #{response}.")
52
+ end
53
+ end
54
+
55
+ def router_status_info(nicknames=nil,fingerprints=nil)
56
+ statuses = ""
57
+
58
+ p "start!"
59
+
60
+ if nicknames
61
+ nicknames.each do |name|
62
+ send("getinfo ns/name/#{name}")
63
+ statuses << @sock.gets
64
+ end
65
+ while not @sock.eof?
66
+ statuses << @sock.gets
67
+ end
68
+ end
69
+ p "done!"
70
+ Onion::RouterList.new(statuses)
71
+ end
72
+
73
+ def get_stream_status
74
+ send("getinfo stream-status")
75
+ result = []
76
+ line = @sock.gets
77
+ while line != "250 OK\r\n"
78
+ unless line == "250+stream-status=\r\n" or line == ".\r\n"
79
+ line = line.gsub("250-stream-status=", "")
80
+ result << line
81
+ end
82
+ line = @sock.gets
83
+ end
84
+ return result
85
+ end
86
+ end
87
+ end
@@ -1,4 +1,8 @@
1
1
  module Onion
2
+ autoload :Circuit, 'onion/elements/circuit'
3
+ autoload :CircuitList, 'onion/elements/circuit_list'
2
4
  autoload :Router, 'onion/elements/router'
3
5
  autoload :RouterList, 'onion/elements/router_list'
6
+ autoload :Stream, 'onion/elements/stream'
7
+ autoload :StreamList, 'onion/elements/stream_list'
4
8
  end
@@ -0,0 +1,11 @@
1
+ module Onion
2
+ # Onion::Circuit is an object representing a Tor circuit.
3
+ class Circuit
4
+ attr_reader :id, :routers
5
+
6
+ def initialize(id, routers)
7
+ @id = id
8
+ @routers = routers
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module Onion
2
+ # Onion::CircuitList is a list of Onion::Circuit objects.
3
+ class CircuitList
4
+ attr_reader :circuits
5
+ def initialize(text)
6
+ if text.blank?
7
+ @circuits = []
8
+ return self
9
+ end
10
+ parser = Onion::CircuitListsParser.new
11
+ if nodes = parser.parse(text)
12
+ @circuits = nodes.circuits
13
+ else
14
+ raise Exception, "Couldn't parse #{text} b/c #{parser.failure_reason}."
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,10 +1,10 @@
1
1
  module Onion
2
2
  # Onion::Router is a Ruby object representing a Tor router.
3
3
  class Router
4
- attr_reader :nick, :id_key_hash, :status_flags
5
- def initialize(nick, id_key_hash, status_flags)
4
+ attr_reader :nick, :fingerprint, :status_flags
5
+ def initialize(nick, fingerprint, status_flags)
6
6
  @nick = nick # Nickname of the router.
7
- @id_key_hash = id_key_hash # Hash of the router identity key.
7
+ @fingerprint = fingerprint # Fingerprint of the router.
8
8
  @status_flags = status_flags # Array of strings with status flags.
9
9
  end
10
10
 
@@ -1,7 +1,5 @@
1
1
  module Onion
2
- # Onion::RouterList is a list of Onion::Router objects. On initialization,
3
- # the text given is parsed with Onion::RouterListParser. The text should be
4
- # in the router status format specified by the directory protocol 2.0 spec.
2
+ # Onion::RouterList is a list of Onion::Router objects.
5
3
  class RouterList
6
4
  attr_reader :routers
7
5
  def initialize(text)
@@ -9,11 +7,11 @@ module Onion
9
7
  @routers = []
10
8
  return self
11
9
  end
12
- parser = Onion::RouterListsParser.new
10
+ parser = Onion::RouterStatusesParser.new
13
11
  if nodes = parser.parse(text)
14
12
  @routers = nodes.routers
15
13
  else
16
- raise Exception "Couldn't parse #{text} b/c #{parser.failure_reason}."
14
+ raise Exception, "Couldn't parse #{text} b/c #{parser.failure_reason}."
17
15
  end
18
16
  end
19
17
 
@@ -29,7 +27,7 @@ module Onion
29
27
 
30
28
  # Returns the routers that are neither guard or exit routers.
31
29
  def middlemen
32
- @routers.filter { |r| r.guard? or r.exit? }
30
+ @routers.reject { |r| r.guard? or r.exit? }
33
31
  end
34
32
  end
35
33
  end
@@ -0,0 +1,12 @@
1
+ module Onion
2
+ # Onion::Circuit is an object representing a Tor stream.
3
+ class Stream
4
+ attr_reader :stream_id, :circuit_id
5
+
6
+ def initialize(stream_id, circuit_id)
7
+ @stream_id = stream_id
8
+ @circuit_id = circuit_id
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Onion
2
+ # Onion::StreamList is a list of Onion::Stream objects.
3
+ class StreamList
4
+ attr_reader :streams
5
+ def initialize(text)
6
+ if text.blank?
7
+ @streams = []
8
+ return self
9
+ end
10
+
11
+ parser = Onion::StreamListsParser.new
12
+ if nodes = parser.parse(text)
13
+ @streams = nodes.streams
14
+ else
15
+ raise Exception, "Couldn't parse #{text} b/c #{parser.failure_reason}."
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,491 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module Onion
5
+ module CircuitLists
6
+ include Treetop::Runtime
7
+
8
+ def root
9
+ @root ||= :circuit_list
10
+ end
11
+
12
+ include Common
13
+
14
+ module CircuitList0
15
+ def circuits
16
+ circuits = []
17
+ self.elements.each do |circuit|
18
+ if circuit.respond_to? :p
19
+ to_process = [circuit.p.first]
20
+ counter = 1
21
+ circuit.p.rest.elements.each do |e|
22
+ to_process << e.LongName
23
+ end
24
+ routers = []
25
+ to_process.each do |router|
26
+ if router.respond_to? :n
27
+ routers << Router.new(router.n.text_value.strip, router.f.text_value.strip, nil)
28
+ else
29
+ routers << Router.new(nil, nil, nil)
30
+ end
31
+ end
32
+ circuits << Circuit.new(circuit.id.text_value.strip, routers)
33
+ else
34
+ circuits << Circuit.new(circuit.id.text_value.strip, [])
35
+ end
36
+ end
37
+ return circuits
38
+ end
39
+ end
40
+
41
+ def _nt_circuit_list
42
+ start_index = index
43
+ if node_cache[:circuit_list].has_key?(index)
44
+ cached = node_cache[:circuit_list][index]
45
+ if cached
46
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
47
+ @index = cached.interval.end
48
+ end
49
+ return cached
50
+ end
51
+
52
+ s0, i0 = [], index
53
+ loop do
54
+ r1 = _nt_circuit_list_entry
55
+ if r1
56
+ s0 << r1
57
+ else
58
+ break
59
+ end
60
+ end
61
+ if s0.empty?
62
+ @index = i0
63
+ r0 = nil
64
+ else
65
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
66
+ r0.extend(CircuitList0)
67
+ end
68
+
69
+ node_cache[:circuit_list][start_index] = r0
70
+
71
+ r0
72
+ end
73
+
74
+ module CircuitListEntry0
75
+ def id
76
+ elements[0]
77
+ end
78
+
79
+ def SP1
80
+ elements[1]
81
+ end
82
+
83
+ def s
84
+ elements[2]
85
+ end
86
+
87
+ def SP2
88
+ elements[3]
89
+ end
90
+
91
+ def p
92
+ elements[4]
93
+ end
94
+
95
+ def SP3
96
+ elements[5]
97
+ end
98
+
99
+ def purp
100
+ elements[7]
101
+ end
102
+
103
+ end
104
+
105
+ module CircuitListEntry1
106
+ def id
107
+ elements[0]
108
+ end
109
+
110
+ def SP1
111
+ elements[1]
112
+ end
113
+
114
+ def s
115
+ elements[2]
116
+ end
117
+
118
+ def SP2
119
+ elements[3]
120
+ end
121
+
122
+ def p
123
+ elements[4]
124
+ end
125
+
126
+ end
127
+
128
+ module CircuitListEntry2
129
+ def id
130
+ elements[0]
131
+ end
132
+
133
+ def SP
134
+ elements[1]
135
+ end
136
+
137
+ def s
138
+ elements[2]
139
+ end
140
+
141
+ end
142
+
143
+ def _nt_circuit_list_entry
144
+ start_index = index
145
+ if node_cache[:circuit_list_entry].has_key?(index)
146
+ cached = node_cache[:circuit_list_entry][index]
147
+ if cached
148
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
149
+ @index = cached.interval.end
150
+ end
151
+ return cached
152
+ end
153
+
154
+ i0 = index
155
+ i1, s1 = index, []
156
+ r2 = _nt_CircuitID
157
+ s1 << r2
158
+ if r2
159
+ r3 = _nt_SP
160
+ s1 << r3
161
+ if r3
162
+ r4 = _nt_CircStatus
163
+ s1 << r4
164
+ if r4
165
+ r5 = _nt_SP
166
+ s1 << r5
167
+ if r5
168
+ r6 = _nt_Path
169
+ s1 << r6
170
+ if r6
171
+ r7 = _nt_SP
172
+ s1 << r7
173
+ if r7
174
+ if has_terminal?("PURPOSE=", false, index)
175
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 8))
176
+ @index += 8
177
+ else
178
+ terminal_parse_failure("PURPOSE=")
179
+ r8 = nil
180
+ end
181
+ s1 << r8
182
+ if r8
183
+ r9 = _nt_Purpose
184
+ s1 << r9
185
+ if r9
186
+ if has_terminal?("\n", false, index)
187
+ r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
188
+ @index += 1
189
+ else
190
+ terminal_parse_failure("\n")
191
+ r10 = nil
192
+ end
193
+ s1 << r10
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ if s1.last
203
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
204
+ r1.extend(CircuitListEntry0)
205
+ else
206
+ @index = i1
207
+ r1 = nil
208
+ end
209
+ if r1
210
+ r0 = r1
211
+ else
212
+ i11, s11 = index, []
213
+ r12 = _nt_CircuitID
214
+ s11 << r12
215
+ if r12
216
+ r13 = _nt_SP
217
+ s11 << r13
218
+ if r13
219
+ r14 = _nt_CircStatus
220
+ s11 << r14
221
+ if r14
222
+ r15 = _nt_SP
223
+ s11 << r15
224
+ if r15
225
+ r16 = _nt_Path
226
+ s11 << r16
227
+ if r16
228
+ if has_terminal?("\n", false, index)
229
+ r17 = instantiate_node(SyntaxNode,input, index...(index + 1))
230
+ @index += 1
231
+ else
232
+ terminal_parse_failure("\n")
233
+ r17 = nil
234
+ end
235
+ s11 << r17
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+ if s11.last
242
+ r11 = instantiate_node(SyntaxNode,input, i11...index, s11)
243
+ r11.extend(CircuitListEntry1)
244
+ else
245
+ @index = i11
246
+ r11 = nil
247
+ end
248
+ if r11
249
+ r0 = r11
250
+ else
251
+ i18, s18 = index, []
252
+ r19 = _nt_CircuitID
253
+ s18 << r19
254
+ if r19
255
+ r20 = _nt_SP
256
+ s18 << r20
257
+ if r20
258
+ r21 = _nt_CircStatus
259
+ s18 << r21
260
+ if r21
261
+ if has_terminal?("\n", false, index)
262
+ r22 = instantiate_node(SyntaxNode,input, index...(index + 1))
263
+ @index += 1
264
+ else
265
+ terminal_parse_failure("\n")
266
+ r22 = nil
267
+ end
268
+ s18 << r22
269
+ end
270
+ end
271
+ end
272
+ if s18.last
273
+ r18 = instantiate_node(SyntaxNode,input, i18...index, s18)
274
+ r18.extend(CircuitListEntry2)
275
+ else
276
+ @index = i18
277
+ r18 = nil
278
+ end
279
+ if r18
280
+ r0 = r18
281
+ else
282
+ @index = i0
283
+ r0 = nil
284
+ end
285
+ end
286
+ end
287
+
288
+ node_cache[:circuit_list_entry][start_index] = r0
289
+
290
+ r0
291
+ end
292
+
293
+ module Path0
294
+ def LongName
295
+ elements[1]
296
+ end
297
+ end
298
+
299
+ module Path1
300
+ def first
301
+ elements[0]
302
+ end
303
+
304
+ def rest
305
+ elements[1]
306
+ end
307
+ end
308
+
309
+ def _nt_Path
310
+ start_index = index
311
+ if node_cache[:Path].has_key?(index)
312
+ cached = node_cache[:Path][index]
313
+ if cached
314
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
315
+ @index = cached.interval.end
316
+ end
317
+ return cached
318
+ end
319
+
320
+ i0, s0 = index, []
321
+ r1 = _nt_LongName
322
+ s0 << r1
323
+ if r1
324
+ s2, i2 = [], index
325
+ loop do
326
+ i3, s3 = index, []
327
+ if has_terminal?(",", false, index)
328
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
329
+ @index += 1
330
+ else
331
+ terminal_parse_failure(",")
332
+ r4 = nil
333
+ end
334
+ s3 << r4
335
+ if r4
336
+ r5 = _nt_LongName
337
+ s3 << r5
338
+ end
339
+ if s3.last
340
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
341
+ r3.extend(Path0)
342
+ else
343
+ @index = i3
344
+ r3 = nil
345
+ end
346
+ if r3
347
+ s2 << r3
348
+ else
349
+ break
350
+ end
351
+ end
352
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
353
+ s0 << r2
354
+ end
355
+ if s0.last
356
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
357
+ r0.extend(Path1)
358
+ else
359
+ @index = i0
360
+ r0 = nil
361
+ end
362
+
363
+ node_cache[:Path][start_index] = r0
364
+
365
+ r0
366
+ end
367
+
368
+ def _nt_CircStatus
369
+ start_index = index
370
+ if node_cache[:CircStatus].has_key?(index)
371
+ cached = node_cache[:CircStatus][index]
372
+ if cached
373
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
374
+ @index = cached.interval.end
375
+ end
376
+ return cached
377
+ end
378
+
379
+ i0 = index
380
+ if has_terminal?("LAUNCHED", false, index)
381
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 8))
382
+ @index += 8
383
+ else
384
+ terminal_parse_failure("LAUNCHED")
385
+ r1 = nil
386
+ end
387
+ if r1
388
+ r0 = r1
389
+ else
390
+ if has_terminal?("BUILT", false, index)
391
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 5))
392
+ @index += 5
393
+ else
394
+ terminal_parse_failure("BUILT")
395
+ r2 = nil
396
+ end
397
+ if r2
398
+ r0 = r2
399
+ else
400
+ if has_terminal?("EXTENDED", false, index)
401
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 8))
402
+ @index += 8
403
+ else
404
+ terminal_parse_failure("EXTENDED")
405
+ r3 = nil
406
+ end
407
+ if r3
408
+ r0 = r3
409
+ else
410
+ if has_terminal?("FAILED", false, index)
411
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 6))
412
+ @index += 6
413
+ else
414
+ terminal_parse_failure("FAILED")
415
+ r4 = nil
416
+ end
417
+ if r4
418
+ r0 = r4
419
+ else
420
+ if has_terminal?("CLOSED", false, index)
421
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 6))
422
+ @index += 6
423
+ else
424
+ terminal_parse_failure("CLOSED")
425
+ r5 = nil
426
+ end
427
+ if r5
428
+ r0 = r5
429
+ else
430
+ @index = i0
431
+ r0 = nil
432
+ end
433
+ end
434
+ end
435
+ end
436
+ end
437
+
438
+ node_cache[:CircStatus][start_index] = r0
439
+
440
+ r0
441
+ end
442
+
443
+ def _nt_Purpose
444
+ start_index = index
445
+ if node_cache[:Purpose].has_key?(index)
446
+ cached = node_cache[:Purpose][index]
447
+ if cached
448
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
449
+ @index = cached.interval.end
450
+ end
451
+ return cached
452
+ end
453
+
454
+ i0 = index
455
+ if has_terminal?("GENERAL", false, index)
456
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 7))
457
+ @index += 7
458
+ else
459
+ terminal_parse_failure("GENERAL")
460
+ r1 = nil
461
+ end
462
+ if r1
463
+ r0 = r1
464
+ else
465
+ if has_terminal?("CONTROLER", false, index)
466
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 9))
467
+ @index += 9
468
+ else
469
+ terminal_parse_failure("CONTROLER")
470
+ r2 = nil
471
+ end
472
+ if r2
473
+ r0 = r2
474
+ else
475
+ @index = i0
476
+ r0 = nil
477
+ end
478
+ end
479
+
480
+ node_cache[:Purpose][start_index] = r0
481
+
482
+ r0
483
+ end
484
+
485
+ end
486
+
487
+ class CircuitListsParser < Treetop::Runtime::CompiledParser
488
+ include CircuitLists
489
+ end
490
+
491
+ end