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

<b>Installation instruction </b>

Run "gem install tor_extend-0.0.1.gem"

If internet connection is available, it will install the Bendiken's Tor gem as well if it is not already installed. As well as socksify
For offline users, obtain a local copy of the Tor gem and install before proceeding. 
Visit Bendiken's page[http://cypherpunk.rubyforge.org/tor/] for details on .

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

<b>Examples</b>.

- How to use Tor control

    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/"
    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/}
            mytor.get_bridges(url,cachedf)
        else
            sleep(2)
            puts mytor.get_bridges( url, cachedf )    #bridges are added to the database automatically
        end 
    }


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