Class: Tor::Circuit

Inherits:
Object
  • Object
show all
Defined in:
lib/circuits.rb

Overview

This is a single (sub-)circuit object over exactly 3 nodes.Suitable nodes must be chosesn to allow the creation of a successful circuit.

Instance Method Summary collapse

Constructor Details

#initialize(ctrller_config, circuitarray, proxyconfig) ⇒ Circuit

Initialises a Tor::Circuit instance variable with attibutes including the @built, @cirnum and @torcontroller

Use nil for proxyport if Tor is running already.


8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/circuits.rb', line 8

def initialize(ctrller_config, circuitarray, proxyconfig)
    @built = true
	    @closed = true
    @circuitarray = circuitarray
    @cirnum = nil
    @ctrller_config = ctrller_config
    @proxyconfig = proxyconfig
    if @proxyconfig.nil?                  # When proxyconfig.nil?, then it's an existing 
        @launched = true
        @torcontroller = Tor::TController.new(@ctrller_config)
    else
 @launched = false
    end
end

Instance Method Details

#attach_streamsObject

This tries to attach all streams to the circuit.



164
165
166
167
168
# File 'lib/circuits.rb', line 164

def attach_streams
    controller.newstreams.each{|z|
        controller.attach_stream(z,@cirnum)
    }
end

#built?Boolean

This methods checks if the circuit has been built. It only reflects a single segment of the circuit

Returns:

  • (Boolean)


76
77
78
79
80
81
82
# File 'lib/circuits.rb', line 76

def built?
    if defined?(@torcontroller)     # cant check built? before launching.
        @built = ! @torcontroller.cir_status.detect{|z| z =~ /#{@cirnum} BUILT/   }.nil? if not @cirnum.nil?
    else
        false
    end 
end

#circuitObject

Returns the nodes that make up the circuit.



24
25
26
# File 'lib/circuits.rb', line 24

def circuit
           @circuitarray
end

#cirnumObject

Returns the circuit number. Returns 0 if it has not been attached



36
37
38
# File 'lib/circuits.rb', line 36

def cirnum
           @cirnum
end

#close_appsObject

Closes the Tor and Polipo launched by the instance of this class, and removes the tmp directory



152
153
154
155
156
157
158
159
160
161
# File 'lib/circuits.rb', line 152

def close_apps
    if defined?(@pid)
        @torcontroller.signal("SHUTDOWN")
        Process.kill("KILL",@pid[:polipo])
        FileUtils.remove_entry_secure @path # srm path ( delete tmp directory recursively )
        @launched=false
        @built=false
    end
    @closed = true
end

#closecirObject

Closes the circuit



146
147
148
149
# File 'lib/circuits.rb', line 146

def closecir
    @torcontroller.closecircuit(@cirnum) if ! @cirnum.nil?
    @built=false
end

#closed?Boolean

Checks if the application instances for Tor and polipo have been closed

Returns:

  • (Boolean)


41
42
43
# File 'lib/circuits.rb', line 41

def closed?
	    @closed
end

#controllerObject

Returns the Tor:Controller used for the circuit.



29
30
31
32
33
# File 'lib/circuits.rb', line 29

def controller
           if defined?(@torcontroller)
               @torcontroller
           end
end

#extendObject

This creates / builds the actual circuit using @circuitarray.



46
47
48
49
50
51
52
53
# File 'lib/circuits.rb', line 46

def extend
    if defined?(@torcontroller)
        @cirnum = @torcontroller.extendcir(0,@circuitarray)
    else
            puts "launch before extending\n"
            return nil
    end
end

#get_bridges(cacheddesc, *config) ⇒ Object

Tor::Circuit.get_bridges( Tor::Cacheddesc, Tor::Circuit.:proxytype=>‘polipo’,:proxyport=>8118,:proxyaddr=>‘127:proxytype=>‘polipo’,:proxyport=>8118,:proxyaddr=>‘127.0:proxytype=>‘polipo’,:proxyport=>8118,:proxyaddr=>‘127.0.0:proxytype=>‘polipo’,:proxyport=>8118,:proxyaddr=>‘127.0.0.1’ )

cachedDesc can be nil if the Google Earth file is not needed.


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/circuits.rb', line 181

def get_bridges(cacheddesc, *config)
    url = URI.parse "https://bridges.torproject.org/"
    if config.empty?
        proxyconfig={:type=>'polipo' ,:port=>8118,:addr=>'127.0.0.1'}
    else
        proxyconfig = config[0]
    end

    if ! defined?(@myhttperrors)
         @myhttperrors=0
    end
    case proxyconfig[:type]
      when /none/i,nil
        http_session=Net::HTTP.new(url.host,url.port)
      when /socks/i,/tor/i
        http_session=Net::HTTP.SOCKSProxy(proxyconfig[:addr], proxyconfig[:port]).new(url.host,url.port)
      when /http/i,/https/i,/polipo/i
        http_session=Net::HTTP::Proxy(proxyconfig[:addr], proxyconfig[:port]).new(url.host,url.port)
    end

    if url.scheme=="https"
        http_session.use_ssl = true
        http_session.verify_mode = OpenSSL::SSL::VERIFY_NONE
    else
        http_session.use_ssl=false
    end
    bridges=[]
    # Rescue from http error
    begin
        resp = http_session.get2 url.path # Let Tor choose circuit itself
        # Additional code will be added shortly to attach the stream to the circuit directly
        puts "#{resp.code} HTTP response"
        # puts resp.body
        respcode= resp.code=="200" ? 200:nil
        if resp.code == "200"
            torbridgeip=resp.body.scan(/\d+\.\d+\.\d+\.\d+\:\d+/)
            torbridgeip.each{|eachbridge|
                bridgeip,bridgeport= eachbridge.split(':')
                x = Tor::Bridge.where(:ipaddr=>bridgeip, :port =>bridgeport)
                if x.empty?
                    if cacheddesc.nil? or (bridge_geoip = cacheddesc.get_geoiprecord(bridgeip)).nil?
                        Tor::Bridge.create(:ipaddr=>bridgeip, :port =>bridgeport,
                                              :lat=>0, :lng=>0)
                    else
                        Tor::Bridge.create(:ipaddr=>bridgeip, :port =>bridgeport,
                                              :lat=>bridge_geoip.latitude.to_f,
                                              :lng=>bridge_geoip.longitude.to_f )
                    end
                end
            }
        end
     bridges = torbridgeip
    rescue
        @myhttperrors +=1
        respcode=nil
        bridges=[]
    end
    bridges.nil? ? [] : bridges #Return array of all bridges
end

#launchObject

Run this after initializing a Tor::Circuit instaance. This writes the following Tor configuration options to a temp file if proxyport not nil: Write HttpProxy, HttpsProxy , DataDirectory /tmp/dir, __LeaveStreamUnattached 1.

This also writes polipoo configuration to a temporary file and executes polipo 
It executes Tor and polipo if proxyport is not nil
Note: The applicaitons will continue running even if the ruby shell is exited. The Circuit.close_apps method can be used to terminate the application and remove the temp dir


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/circuits.rb', line 93

def launch
    # should I use popen3? dont need stdin and stderror from tor or polipo
    if ! @proxyconfig.nil?
        @path = Dir.mktmpdir if not defined?(@path)
        @pid= {}
        torrc_filename = File.join(@path,"torrc")
        torrc = File.new( torrc_filename, "w")
        # populate torrc
        socks_port = @ctrller_config[:port] - 1
        torrc.puts "ControlPort #{@ctrller_config[:port]}\n"
        torrc.puts "DataDirectory #{@path}\n"
        torrc.puts "HttpProxy 127.0.0.1:#{@proxyconfig - 1}\n"      # Use the preceeding polipo port
        torrc.puts "HttpsProxy 127.0.0.1:#{@proxyconfig - 1}\n"
        torrc.puts "RunAsDaemon 1\n"
        torrc.puts "SocksPort #{socks_port}\n"
        torrc.puts "SocksListenAddress 127.0.0.1\n"
        # can add a random password and call tor --hashcontrolpassword read stdout..but feels like long thing. Or ruby implementation of hash
        torrc.close
        puts "Torrc config written \n"
        #puts @tor_io=Process.spawn("tor","-f",torrc_filename)     # stdin, stdout, stderr, thread
        #puts @tor_io=Open3.popen3("tor","-f",torrc_filename)     # stdin, stdout, stderr, thread
        @pid[:tor] = Process.spawn("tor","-f",torrc_filename)
        sleep(7) # wait 7 seconds to download cached consensus and descriptors. -Consider copuing the existing one from the previous directory
		# check here # **************** why sleep? can I use the cached desc from the first tor process
        @torcontroller = Tor::TController.new(@ctrller_config)
        # write proxyconfig to polipo
        polipo_filename = File.join(@path,"polipo_config")
        polipo_config = File.new( polipo_filename , "w")
        polipo_config.puts "proxyPort = #{@proxyconfig}\n"
        polipo_config.puts "socksParentProxy = localhost:#{socks_port}\n"
        polipo_config.puts "socksProxyType = socks5\n"
        polipo_config.puts "daemonise = true\n"
        polipo_log = File.join(@path,"polipo_log")
        polipo_pid = File.join(@path,"polipo.pid")
        polipo_forbid = File.join(@path,"forbidden")
        polipo_config.puts "logFile = #{polipo_log}\n"
        polipo_config.puts "pidFile = #{polipo_pid}\n"
        polipo_config.puts "forbiddenFile = #{polipo_forbid}\n"
        polipo_config.close
        @pid[:polipo]= Process.spawn( "polipo", "-c", polipo_filename)
    end
    @torcontroller = Tor::TController.new(@ctrller_config)
    # @path = @torcontroller.getconf("DataDirectory") when proxyconfig==nil # dont need path for the first Tor
    @torcontroller.setconf("__DisablePredictedCircuits",1)
    @torcontroller.setconf("newcircuitperiod",999999999)
    @torcontroller.setconf("maxcircuitdirtiness",999999999)
    @torcontroller.setconf("MaxOnionsPending",0)
    @torcontroller.setconf("__LeaveStreamsUnattached",1)
    @launched = true
    @closed = false
end

#launched?Boolean

Checks if the launched method has been called

Returns:

  • (Boolean)


85
86
87
# File 'lib/circuits.rb', line 85

def launched?
    @launched
end

#startObject

This attaches all streams on this controller to a prebuilt circuit that has been



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/circuits.rb', line 56

def start
    if ! launched?
        launch
    end
    if ! built?
        extend
    end
    # sleep(3.5)            ###################### wait 3 seconds before checking if built. # May not be needed
    # use events here instead to (build) no matter what. or (launch and build). If built, attach existing streams from preceeding subcircuit
    if built?
        streams_array = @torcontroller.newstreams
		if ! streams_array.empty? and ! @cirnum.nil?
            streams_array.each{|each_stream|
                @torcontroller.attach_stream(each_stream, @cirnum)
     }
		end
    end
end