class Tor::Circuit

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

Public Class Methods

new(ctrller_config, circuitarray, proxyconfig) click to toggle source

Initialises a Tor::Circuit instance variable with attibutes including the @built, @cirnum and @torcontroller Use nil for proxyport if Tor is running already.

# 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

Public Instance Methods

attach_streams() click to toggle source

This tries to attach all streams to the circuit.

# File lib/circuits.rb, line 164
def attach_streams
    controller.newstreams.each{|z|
        controller.attach_stream(z,@cirnum)
    }
end
built?() click to toggle source

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

# 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
circuit() click to toggle source

Returns the nodes that make up the circuit.

# File lib/circuits.rb, line 24
def circuit
    @circuitarray
end
cirnum() click to toggle source

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

# File lib/circuits.rb, line 36
def cirnum
    @cirnum
end
close_apps() click to toggle source

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

# File lib/circuits.rb, line 152
def close_apps
    if defined?(@pid)
        @torcontroller.signal("SHUTDOWN")
        Process.kill("SIGHUP",@pid[:polipo])
        FileUtils.remove_entry_secure @path # srm path ( delete tmp directory recursively )
        @launched=false
        @built=false
    end
    @closed = true
end
closecir() click to toggle source

Closes the circuit

# File lib/circuits.rb, line 146
def closecir
    @torcontroller.closecircuit(@cirnum) if ! @cirnum.nil?
    @built=false
end
closed?() click to toggle source

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

# File lib/circuits.rb, line 41
def closed?
    @closed
end
controller() click to toggle source

Returns the Tor:Controller used for the circuit.

# File lib/circuits.rb, line 29
def controller
    if defined?(@torcontroller)
        @torcontroller
    end
end
extend() click to toggle source

This creates / builds the actual circuit using @circuitarray.

# 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) click to toggle source

This gets 3 bridge IP addresses from the Tor bridge website. proxyconfig is optional. default proxyconfig = {:type=>‘polipo’ ,:port=>8118,:addr=>‘127.0.0.1’} The return format is fo the form:

[HTTPcode, [{:bridgeip, :bridgeport},{:bridgeip, :bridgeport},{:bridgeip, :bridgeport}]. The types can be one of ‘tor’, ‘polipo’, ‘socks’, ‘http’, ‘https’,nil, ‘none’.

Get 3 bridges

Tor::Circuit.get_bridges( Tor::Cacheddesc, {:proxytype=>'polipo',:proxyport=>8118,:proxyaddr=>'127.0.0.1'}  )

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

# 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/,nil
        http_session=Net::HTTP.new(url.host,url.port)
      when /socks/,/tor/
        http_session=Net::HTTP.SOCKSProxy(proxyconfig[:addr], proxyconfig[:port]).new(url.host,url.port)
      when /http/,/https/,/polipo/
        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
launch() click to toggle source

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 #close_apps method can be used to terminate the application and remove the temp dir

# 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?() click to toggle source

Checks if the launched method has been called

# File lib/circuits.rb, line 85
def launched?
    @launched
end
start() click to toggle source

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

# 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