/**
* OnionCoffee - Anonymous Communication through TOR Network
* Copyright (C) 2005-2007 RWTH Aachen University, Informatik IV
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package TorJava;
import java.io.IOException;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import TorJava.Common.TorException;
/**
* maintains the list of active TLS-connections to Tor nodes.
*
* @author Lexi Pimenidis
* @author Andriy Panchenko
* @version unstable
*/
class FirstNodeHandler {
ConcurrentHashMap<String,TLSConnection> tls;
static Random rnd;
// nicknames of currently used nodes in circuits as key, # of cirs - value
static ConcurrentHashMap<String,Integer> currentlyUsedNodes;
Tor tor;
/**
* initialize Handler of TLSConnections
*/
FirstNodeHandler(Tor tor) throws IOException {
tls = new ConcurrentHashMap<String,TLSConnection>();
rnd = new Random();
currentlyUsedNodes = new ConcurrentHashMap<String,Integer>();
this.tor = tor;
}
/**
* return a pointer to a TLS-connection to a certain node. if there is
* none, it is created and returned.
*
* @param server
* the node to connect to
* @return the TLS connection
*/
TLSConnection get_connection(Server server) throws IOException,
TorException {
if (server == null)
throw new TorException("FirstNodeHandler: server is NULL");
// check if TLS-connections to node established
if (tls.containsKey(server.nickname)) {
return (TLSConnection) tls.get(server.nickname);
} else {
// build otherwise
Logger.logTLS(Logger.VERBOSE,"FirstNodeHandler: TLS connection to " + server.nickname);
TLSConnection t = new TLSConnection(server, tor.privateKeyHandler);
Logger.logTLS(Logger.VERBOSE,"FirstNodeHandler: Adding to TLS: " + server.nickname);
tls.put(server.nickname, t);
return t;
}
}
/**
* closes all TLS connections
*
* @param force
* set to false, if circuits shall be terminated gracefully
*/
void close(boolean force) {
Iterator<String> i = tls.keySet().iterator();
while (i.hasNext()) {
TLSConnection t = (TLSConnection) tls.get(i.next());
t.close(force);
}
}
Circuit provideSuitableNewCircuit(TCPStreamProperties sp)
throws IOException
{
for (int retries = 0; retries < TorConfig.retriesConnect; ++retries) {
try {
return new Circuit(tor,this, tor.dir, sp);
}
catch (InterruptedException e) { /* no, do nothing */ }
catch(TorException e) { /* continue trying */ }
}
return null;
}
/**
* used to return a number of circuits to a target. established a new circuit or uses an existing one
*
* @param sp gives some basic restrains
* @param forHiddenService if set to true, use circuit that is unused and don't regard exit-policies
* @param force_new create new circuit anyway
*/
Circuit[] provideSuitableCircuits(TCPStreamProperties sp, boolean forHiddenService) throws
IOException {
Logger.logCircuit(Logger.VERBOSE, "FirstNodeHandler.provideSuitableCircuits: called for " + sp.hostname);
// list all suiting circuits in a vector
int numberOfExistingCircuits = 0;
Vector<Circuit> allCircs = new Vector<Circuit>(10,10);
int rankingSum = 0;
Iterator<String> it = this.tls.keySet().iterator();
Logger.logCircuit(Logger.VERBOSE, "FirstNodeHandler.provideSuitableCircuits: " + this.tls.size() + " circuits");
while (it.hasNext()) {
TLSConnection tls = (TLSConnection) this.tls.get(it.next());
Iterator<Integer> i2 = tls.circuits.keySet().iterator();
while (i2.hasNext()) {
try {
Circuit circuit = tls.circuits.get(i2.next());
++numberOfExistingCircuits;
if (circuit.established && (!circuit.closed) && tor.dir.isCompatible(circuit, sp, forHiddenService)){
allCircs.add(circuit);
rankingSum += circuit.ranking;
}
} catch (TorException e) { /* do nothing, just try next circuit */}
}
}
// sort circuits (straight selection... O(n^2)) by
// - wether they contained a stream to the specific address
// - ranking (stochastically!)
// - implicit: wether they haven't had a stream at all
for(int i=0;i<allCircs.size()-1;++i) {
Circuit c1 = (Circuit)allCircs.get(i);
int min=i;
int min_ranking = c1.ranking;
if (min_ranking==0) min_ranking=1;
boolean min_points_to_addr = c1.streamHistory.contains(sp.hostname);
for(int j=i+1;j<allCircs.size();++j) {
Circuit thisCirc = (Circuit)allCircs.get(j);
int this_ranking = thisCirc.ranking;
if (this_ranking==0) this_ranking=1;
boolean this_points_to_addr = thisCirc.streamHistory.contains(sp.hostname);
float ranking_quota = this_ranking / min_ranking;
if ((this_points_to_addr && !min_points_to_addr) ||
(rnd.nextFloat() > Math.exp(-ranking_quota))) { // sort stochastically
min = j;
min_ranking = this_ranking;
}
}
if (min>i) {
Circuit temp = allCircs.set(i,allCircs.get(min));
allCircs.set(min,temp);
}
}
// return number of circuits suiting to number of stream-connect retries!
int return_values = sp.connect_retries;
if (allCircs.size()<return_values) return_values=allCircs.size();
if ((return_values==1)&&(numberOfExistingCircuits < TorConfig.circuitsMaximumNumber)) {
// spawn new circuit IN BACKGROUND, unless maximum number of circuits reached
Logger.logCircuit(Logger.VERBOSE, "FirstNodeHandler.provideSuitableCircuits: spawning circuit to " + sp.hostname + " in background");
final TCPStreamProperties spFinal = sp;
Thread spawnInBackground = new Thread() {
public void run() {
try{ new Circuit(tor, tor.fnh, tor.dir, spFinal); }
catch(Exception e){}
}
};
spawnInBackground.setName("FirstNodeHandler.provideSuitableCircuits");
spawnInBackground.start();
} else if ((return_values==0)&&(numberOfExistingCircuits < TorConfig.circuitsMaximumNumber)) {
// spawn new circuit, unless maximum number of circuits reached
Logger.logCircuit(Logger.VERBOSE, "FirstNodeHandler.provideSuitableCircuits: spawning circuit to " + sp.hostname );
Circuit single = provideSuitableNewCircuit(sp);
if (single!=null) {
return_values=1;
allCircs.add(single);
}
}
// copy values
Circuit[] results = new Circuit[return_values];
for(int i=0;i<return_values;++i) {
results[i] = (Circuit)allCircs.get(i);
Logger.logCircuit(Logger.VERBOSE, "FirstNodeHandler.provideSuitableCircuits: Choose Circuit ranking "+results[i].ranking+":"+results[i].print());
}
return results;
}
}
// vim: et