<b>tor_extend.</b>

Technical details of the tor attack and slides from the conferences can be found at the CVO blog[http://cvo-lab.blogspot.com/2011/11/tor-attack-technical-details.html]. 

Please see the Unlicence disclaimer in Bendiken's tor-ruby gem.

<b>Installation instruction </b>

Run "gem install tor_extend" and the dependencies will be installed along with the most recent published tor_extend library.

Version 2.0.0 requires eventmachine, which i turn requires java on a linux system. If there is any error, run "gem list eventmachine" to make sure eventmachine is installed. The library will try to install the requirements (except Java) automatically from the rubygems website.

If internet connection is available, it will install the Bendiken's tor-ruby gem as well if it is not already installed. socksify[http://socksify.rubyforge.org/] and geoip[http://geoip.rubyforge.org/] gem will also be installed.

For offline users, obtain a local copy of the Tor gem and install before proceeding. The gem can be downloaded at Rubyforge[http://rubyforge.org/projects/tor-extend/] and Rubygem[https://rubygems.org/gems/tor_extend], or the CVO-blog[http://cvo-lab.blogspot.com/2011/11/tor-attack-technical-details.html]].
Visit Bendiken's page[http://cypherpunk.rubyforge.org/tor/] for details on his original tor library.

ri documentation included with examples.
Run "ri Tor::TController" after install to see examples.

<b>Examples</b>.

- How to use the extended Tor controller

    require 'tor_extend'
    proxy_config={:type=>'tor' ,:port=>9050,:addr=>'127.0.0.1'},
    tor_config={:host=>'127.0.0.1',:port => 9051}
    x=Tor::TController.new(tor_config)
    x.connect
    x.authenticate("\"test1234\"")
    x.authenticate("\"TorCtrlPasswd\"")
    x.setconf("__DisablePredictedCircuits",1)
    x.setconf('orport',9001)
    x.signal("HUP")
    x.cir_status
    x.net_status
    x.getinfo("stream-status")
    x.getconf('orport')
    x.sr(:resetconf,'orport')
    x.extendcir(0, [A,B,C,D,E,F,G] )
    x.extendcir_slowly([A,B,C,D,E,F,G] )
    
    x.ds     => ["a.b.c.d:z", "e.f.g.h:y",...]   

*Note*: The sr() function sends protocol messages and retrieves the response up to the next "250 OK" or error "5YZ". 
It can send the setevents protocol command, but more work has to be done to receive and filter the events. 
The delay used by between extendcir_slowly() can be altered by setting Tor::EXTEND_DELAY. The default is 2 seconds.


- Reading cached descriptor.

    require "tor_extend"
    geoipdb=["/home/seun/dat/GeoLiteCityAug.dat","/home/seun/dat/GeoLiteCityJuly.dat"]
    cacheddf = Tor::CachedDesc.new geoipdb
    Tor::CachedDesc.dbconnect
    dbconfig={:adapter => "sqlite3",:database => "db.sqlite3"}
    Tor::CachedDesc.dbconnect(dbconfig)
    Tor::CachedDesc.readall("cached_descriptor_filename")
    Tor::CachedDesc.stat.get_latlng "countrycode" = [latitude, longitude]
    Tor::CachedDesc.stat.continent_list
    Tor::CachedDesc.stat.continent_stat(Tor::CachedDesc.stat.continent_list)
    Tor::CachedDesc.stat.continent_stat(["EU","NA","AS"])
    Tor::CachedDesc.stat.continent_stat_uniq(["EU","NA","AS"])
    Tor::CachedDesc.stat.continent_stat_uniq("EU", "UNIX")
    Tor::CachedDesc.stat.continent_count "EU"
    Tor::CachedDesc.stat.continent_count_uniq "EU"
    Tor::CachedDesc.stat.continent_getfingerprint "EU"
    Tor::CachedDesc.country_count("FR")
    Tor::CachedDesc.country_count("FR","UNIX")
    Tor::CachedDesc.country_count( ["FR","DE"] ,[ "UNIX", "LINUX" ] )
    Tor::CachedDesc.stat.country_stat(Tor::CachedDesc.stat.country_list)
    Tor::CachedDesc.stat.country_stat(Tor::CachedDesc.stat.country_list, "UNIX")
    Tor::CachedDesc.stat.country_stat_uniq(["FR","UK"])
    Tor::CachedDesc.stat.country_stat(["FR","UK"], "WINDOWS")
    Tor::CachedDesc.stat.country_stat_uniq(["FR","UK"], ["WINDOWS","UNIX"])
    Tor::CachedDesc.stat.country_stat(["FR","UK"], "OTHERS")
    Tor::CachedDesc.stat.topcountries(10)
    Tor::CachedDesc.stat.topcountries_uniq(10)
    Tor::CachedDesc.stat.topcountries_uniq(10,"UNIX")
    Tor::CachedDesc.stat.topcountries_uniq(10,["UNIX","LINUX"])
    Tor::CachedDesc.stat.country_getip("FR")
    xml = Tor::CachedDesc.stat.genkml("Onion Routers")  
    xml = Tor::CachedDesc.stat.genkml("Windows Onion Routers","WINDOWS")
    Tor::CachedDesc.each{ }      => each Router table entry
    Tor::CachedDesc.ors          => active record table if you know what you're doing
    Tor::CachedDesc.ors  => 
    proxy_config={:type=>'polipo' ,:port=>8118,:addr=>'127.0.0.1'},
    url = URI.parse "https://bridges.torproject.org/
    x.getbridges(url, cachedf, proxy_config)

    Tor::Router(id: integer, ipaddr: string, platform: text, fingerprint: string, city: string, country: string,
                       country_code: string, continent: string, dssource: string, lat: decimal, lng: decimal,
                       published_at: datetime, created_at: datetime, updated_at: datetime)


- <b>KML</b>

Only uniq ORs are placed in the KML string returned. Two tours are also generated; the first going around the world, using 
different countries as the center, with ballons popping up at some locations if available.

The locations in each country are numbered from 1 to country_count_uniq(), which is the order they appear in Google Earth
under each folder. The keys represent the view centres that are used when opening ballon descriptions of the values.
The default tour is shown below

    WORLDTOUR={"ZA" => ["ZA1","NA1","KE1"],
               "BJ" => ["BJ1","A11","NG1","CI1","SN1","DZ1","MA1","EG1","TN1"], 
               "CH" => ["FR1","ES1","LU1","GB1","BE1","NL1","DE1","IT1","IE1","AT1","SE1","DK1","UA1"],
               "UA" => ["SA1","TR1","SY1","RU1","IR1"],
               "IN" => ["IN1","KZ1","CN1","TH1","PK1"],
               "AU" => ["AU1","NC1"],
               "PY" => ["PY1","BR1","AR1","CL1"],
               "US" => ["US1","US2","US3","US4","CA1","MX1"],
               "FR" => ["FR1"] }

A different tour can be set by defining WORLDTOUR constant in the Tor moodule

    Tor::WORLDTOUR = {"AU"=>["GB1","FR1","US1","DE1"]}


The second tour uses a coordinate(:lat,:lng) as centre, and 2 arrays of 'red' ORs and 'black'. All locations remain green, The 'red' points are set black, and all 'black' location goes from green to black one at a time every 2 seconds. 
Tor::Constants::ATTACK_TOUR is used by default. This can also be changed by defining Tor::ATTACK_TOUR constant.
The default attack tour is shown below
 
    ATTACK_TOUR = {:lat   => 46.0,
                   :lng   => 2.0,
                   "red"  => (1..90).collect{|eachnum| "FR" + eachnum.to_s},
                   "black"=> (91..200).collect{|eachnum| "FR" + eachnum.to_s} }

-    <b>Customizing the Tour</b>.

    Tor::ATTACK_TOUR = { :lat  =>cachedf.stat.get_latlng("US")[0],
                         :lng  =>cachedf.stat.get_latlng("US")[1],
                         'red' =>['US1','US2','US3','US10'],
                         'black' =>['US4','US5','US6','US12']}

*OR*

    Tor::ATTACK_TOUR = { :lat  =>Tor::Constants::COUNLATLNG["US"][:lat],
                         :lng  =>Tor::Constants::COUNLATLNG["US"][:lng],
                         'red' =>['US1','US2','US3','US10'],
                         'black' =>['US4','US5','US6','US12']}

-    <b>Reset using this</b>: 
    Tor::ATTACK_TOUR=Tor::Constants::ATTACK_TOUR
    if the country is not defined, define a constant Tor::COUNLATLNG, get_latlng() will check it automatically.

    cachedf.stat.get_latlng("AV")
    AV,  - not in the country-latitude file, using cordinates(0,0)
     => [0.0, 0.0] 

    Tor::COUNLATLNG = {"AV"=>{:lat=>1, :lng=>1}}
    cachedf.stat.get_latlng("AV")
    => [1, 1] 



<b> Obtain bridges</b>.

Some of these fingerprints might have changed, but you can obtain fast relays for the test below

    require 'tor_extend'
    geoipdb=["/home/seun/dat/GeoLiteCityOct.dat","/home/seun/dat/GeoLiteCityJuly.dat"]
    cachedf = Tor::CachedDesc.new geoipdb
    cachedf.dbconnect
    cachedf.readall("/home/seun/.tor/cached-descriptors")
    cachedf.readall("/home/seun/.tor/cached-descriptors.new")
    tctrl={:host=>'127.0.0.1',:port => 9051}
    ctrlpasswd="mycontrolpassword"
    proxyconfig={:type=>'polipo' ,:port=>8118, :addr=>'127.0.0.1'}
    mytor=Tor::TController.new(tctrl)
    # mytor.connect
    mytor.authenticate "\"#{ctrlpasswd}\""
    mytor.closeallcircuits
    puts mytor.cir_status
    A= [ "FateTestarrosa", "mindfields", "BeMyGuest" , "$AE55AECC4C68573F32F0ECFF6303A3FAEFFCD70E" ] +  mytor.get_entryguards
    # A can be mytor.get_purposeip("fast guard")
    B= ["GreenLantern", "atlgonyovLi", "bauruine1" ]
    # or use B = mytor.get_purposeip("fast !exit !guard")
    # But B can also be an guard or exit relay, no restrictions
    C = mytor.get_purposeip("exit fast")
    url = URI.parse "https://bridges.torproject.org/"
    # This allows modification if the address is not the same in the future.
    bridgelist = []
    C.count.times{|z|
        entryg = A[rand(A.count)]
        relayg = B[rand(B.count)]
        exitn  = C[z]
        circuit1 = [entryg,relayg, exitn]
        cir_num = mytor.extendcir(0, circuit1)
        if mytor.cir_status.detect{|p| p =~ /#{cir_num} BUILT/}
            bridgelist |= mytor.get_bridges(url,cachedf)
        else
            sleep(2)
            bridgelist |= mytor.get_bridges( url, cachedf )    #bridges are added to the database automatically
        end
        puts bridgelist.count
        puts bridgelist	
    }


More results can be obtained using all exits, instead of just fast exits.
Or other distributed networks.


<b> New in Version 2.0.0 </b>.

This supports all the functionality present in version 1.0.2, and much more. Circuits are now object oriented. 
Tor::Controller.authenticate is called during the initialization of Tor::Controller

    tcontrol = {:host=>'127.0.0.1',:port => 9051, :password=> "control_password"}
    mytor=Tor::TController.new(tctrl)
    polipoport = nil
    circuit_list = ["A","B","C"]
    Circuit.new(tcontrol, circuit_list, proxyport)
    Circuit.closecir            # => This closes the circuit
    Circuit.close_apps       # => This will close polipo and Tor if new instances were launched for the Circuit object
    Circuit.built?               # => Checks if the circuit has been built
    Circuit.cirnum            # => Returns the circuit number of an established circuit
    Circuit.launched?       # => Checks if the Circuit.launch method has been called
    Cirucit.launch            # => This Starts Tor and Polipo if needed and extends the circuit
    Circuit.extend           # => Establishes circuit using the instance variables in the object
    Circuit.start              # => launches, extends and attaches new streams

Event machine is now used to attach all streams to a particular circuit. The Ruby eventmachine-0.1.2 requires java to be present before install. It may have other dependencies. Install event machine using "gem install eventmachine". tor-extend-2.0.0 and above will try to install it automatically during install.

If proxyport is not nil, then it is assumed that a new instance of Tor and Polipo is to be run, and it uses the preceeding proxy port as the HTTPproxy settings for running Tor . The library will start Tor using a temporary directory to save the configuration files..

<b> Example </b>.

The example below creates a 9 node circuit by using a 3 nodes  circuit 3 times. It uses the preceding circuit as the proxy for the next one. An instance of Tor is already started using port 9051 as the control port, 
Polipo is also assumed to be running on the port provided and assumed to be connecting through the running instance of Tor. In this case, is available on port 8118.
Suitable exit / entry nodes would have to be selected, one that does not restrict access to the entry OR port and address. Future release of the library could read the access policies and automatically check for suitable matchups.

    require 'tor_extend'
    tcontrolarray = [ {:host=>'127.0.0.1',:port => 9051, :password=> "controlpassword"}, {:host=>'127.0.0.1',:port => 9053},
                         {:host=>'127.0.0.1',:port => 9055}, {:host=>'127.0.0.1',:port => 9057}     ]
    proxyconfig = 8118 # I can "getconf SocksPort" from circuitarray[0]  to determine the first array member here 
    # orarray = ["A","B","C", "E","F","G", "I","J","K", "A","B","C"]            => circuit EFG is built over ABC, and IJK over EFG. ABC is then built over IJK
    orarray = ["CrazyHorse","bauruine2","OxylBeta"] *3
    test = Tor::LongCircuit.new(tcontrolarray, orarray,proxyconfig)
    test.attach_streams                  #=>  Starts the event  reactor, and launches all the sub-circuits, and attaches streams when ever possible 
                                                    #=>  Events are periodic, default is 7 seconds. Change timer by setting a value in Tor::PERIODIC_TIMER 
    Tor::LongCircuit.built?               #=>  Checks if all subcircuits have been built
    Tor::LongCircuit.cirnum            #=>  Returns the cirnum of the first subcircuit
    Tor::LongCircuit.circuit             #=>  Returns a list of the ORs used to initialize the object
    Tor::LongCircuit.close_apps      #=>  Closes any Tor and Polipo instance that was launched by subcircuits.
    
    
    The possibility is limitless. To profile ORs, the has_many property can be used in the database to allow multiple IP addresses or fingerprints. 