/** * 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.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.ListIterator; /** * Management thread * * @author Lexi Pimenidis * @author Michael Koellejan * @version unstable */ class TorBackgroundMgmt extends Thread { static final int MILLISEC = 1000; // general multiplicator for time static int INITIAL_INTERVAL = 3; // time to sleep until first actions static int INTERVAL = 10; // time to wait inbetween working loads static int CIRCUITS_KEEP_ALIVE_INTERVAL = 30; // interval of padding messages on circuits static int STREAMS_KEEP_ALIVE_INTERVAL = 30; // interval of padding messages on streams static int MAX_NUMBER_THREADS = 4; Tor tor; // pointer to main class int number_of_circuits; // at least this amount of circuits should always be // available // upper bound is (number_of_circuits+tor.config.circuitsMaximumNumber) private long now_long; // store the current time private ArrayList<Thread> brtList; // List of background threads (for graceful // close) boolean stopped = false; // As stop() is depreceated we follow the Sun // recomm. DirectoryManager dirman = null; TorBackgroundMgmt(Tor tor, int number_of_circuits, boolean fastStartup) { this.brtList = new ArrayList<Thread>(number_of_circuits); this.tor = tor; this.number_of_circuits = number_of_circuits; this.setName("TorBackgroundMgmt"); now_long = new Date().getTime(); spawn_idle_circuits(number_of_circuits); this.dirman = new DirectoryManager(tor, fastStartup); this.start(); } /** create some empty circuits to have at hand - does so in the background */ private void spawn_idle_circuits(int amount) { // Don't create circuits until not at least a certain fraction of the routers is known if (tor.dir.nodeCounter > 0) { int minDescriptors = Math.min(Math.round(TorConfig.min_percentage*tor.dir.nodeCounter), TorConfig.min_descriptors); if (tor.dir.torServers.size() < Math.max(minDescriptors, TorConfig.route_min_length)) { Logger.logGeneral(Logger.INFO, "Not yet spawning circuits ("+ tor.dir.torServers.size()+"/"+tor.dir.nodeCounter+" descriptors present)"); return; } else Logger.logGeneral(Logger.INFO, "TorBackgroundMgmt.spawn_idle_circuits: Spawn "+amount+" new circuits"); } else { return; } TorKeeper.setComplete(TorKeeper.DESCRIPTORS_FETCHED, true); // Cleanup our background thread list ListIterator<Thread> brtIterator = brtList.listIterator(); while (brtIterator.hasNext()) { Thread brt = brtIterator.next(); if (!brt.isAlive()) brtIterator.remove(); } if ((amount + brtList.size())>MAX_NUMBER_THREADS) { amount = MAX_NUMBER_THREADS - brtList.size(); if (amount < 0) amount = 0; } // Spawn new background threads for (int i = 0; i < amount; ++i) { Thread brt = new Thread() { public void run() { try { // idle threadds should at least allow using port 80 TCPStreamProperties sp = new TCPStreamProperties(); sp.port = 80; new Circuit(tor, tor.fnh, tor.dir, sp); } catch (Exception e) { Logger.logCircuit(Logger.VERBOSE,"TorBackgroundMgmt.spawn_idle_circuits: "+e.getMessage()); } // Common.listAllRunningThreads(); } }; Logger.logGeneral(Logger.RAW_DATA, "TorBackgroundMgmt.spawn_idle_circuits: Circuit-Spawning thread started."); brt.setName("Spawn Idle Thread"); brt.start(); // Common.listAllRunningThreads(); brtList.add(brt); } } /** * sends keep-alive data on circuits */ private void send_keep_alive_packets() { Iterator<String> i = tor.fnh.tls.keySet().iterator(); while (i.hasNext()) { TLSConnection tls = (TLSConnection) tor.fnh.tls.get(i.next()); Iterator<Integer> i2 = tls.circuits.keySet().iterator(); while (i2.hasNext()) { // check if this circuit needs a keep-alive-packet Circuit c = (Circuit) tls.circuits.get(i2.next()); if ((c.established) && (now_long - c.last_cell.getTime() > CIRCUITS_KEEP_ALIVE_INTERVAL * MILLISEC)) { Logger.logGeneral(Logger.RAW_DATA, "TorBackgroundMgmt.send_keep_alive_packets(): Circuit " + c.print()); c.sendKeepAlive(); } // check streams in circuit Iterator<Integer> i3 = c.streams.keySet().iterator(); while (i3.hasNext()) { TCPStream stream = (TCPStream) c.streams.get(i3.next()); if ((stream.established) && (!stream.closed) && (now_long - stream.last_cell.getTime() > STREAMS_KEEP_ALIVE_INTERVAL * MILLISEC)) { Logger.logGeneral(Logger.RAW_DATA, "TorBackgroundMgmt.send_keep_alive_packets(): Stream " + stream.print()); stream.sendKeepAlive(); } } } } } /** * used to determine which (old) circuits can be torn down because there are * enough new circuits. or builds up new circuits, if there are not enough. */ private void idle_circuits() { // count circuits int circuits_total = 0; // all circuits int circuits_alive = 0; // circuits that are building up, or that are established int circuits_established = 0; // established, but not already closed int circuits_closed = 0; // closing down Iterator<String> i = tor.fnh.tls.keySet().iterator(); while (i.hasNext()) { TLSConnection tls = (TLSConnection) tor.fnh.tls.get(i.next()); Iterator<Integer> i2 = tls.circuits.keySet().iterator(); while (i2.hasNext()) { Circuit c = (Circuit) tls.circuits.get(i2.next()); String flag = ""; ++circuits_total; if (c.closed) { flag = "C"; ++circuits_closed; } else { flag = "B"; ++circuits_alive; if (c.established) { flag = "E"; ++circuits_established; }; } Logger.logGeneral(Logger.RAW_DATA,"TorBackgroundMgmt.idle_circuits(): "+flag+" rank "+c.ranking+ " fails "+c.stream_fails+" of "+c.stream_counter+ " TLS "+tls.server.nickname+"/"+c.print()); } } Logger.logGeneral(Logger.RAW_DATA, "TorBackgroundMgmt.idle_circuits(): circuit counts: " + (circuits_alive - circuits_established) + " building, " + circuits_established + " established + " + circuits_closed + " closed = " + circuits_total); // check if enough 'alive' circuits are there if (circuits_established >= 1) TorKeeper.setComplete(TorKeeper.CIRCUITS_AVAILABLE, true); if (circuits_alive < number_of_circuits) { //if (tor.dir.torServers.size()>1) //Logger.logGeneral(Logger.INFO, "TorBackgroundMgmt.idle_circuits(): spawn " + (number_of_circuits - circuits_alive) + " new circuits"); spawn_idle_circuits( (number_of_circuits - circuits_alive) * 3 / 2 ); } else if (circuits_established > number_of_circuits + TorConfig.circuitsMaximumNumber) { // TODO: if for some reason there are too many established circuits. close the oldest ones Logger.logGeneral(Logger.VERBOSE, "TorBackgroundMgmt.idle_circuits(): kill " + (number_of_circuits + TorConfig.circuitsMaximumNumber - circuits_alive) + "new circuits (FIXME)"); } } /** * used to close circuits that are marked for closing, but are still alive. * They are closed, if no more streams are contained. */ private void tear_down_closed_circuits() { Iterator<String> i = tor.fnh.tls.keySet().iterator(); while (i.hasNext()) { TLSConnection tls = (TLSConnection) tor.fnh.tls.get(i.next()); Iterator<Integer> i2 = tls.circuits.keySet().iterator(); while (i2.hasNext()) { Circuit c = (Circuit) tls.circuits.get(i2.next()); // check if stream is establishing but doesn't had any action for a longer period of time Iterator<Integer> i3 = c.streams.keySet().iterator(); while (i3.hasNext()) { TCPStream s = (TCPStream) c.streams.get(i3.next()); long diff = (now_long - s.last_action.getTime()) / MILLISEC; if ((!s.established) || s.closed) { if (diff > (2*TorConfig.queueTimeoutStreamBuildup)) { //System.out.println("close "+diff+" "+s.print()); Logger.logGeneral(Logger.VERBOSE, "TorBackgroundMgmt.tear_down_closed_circuits(): closing stream (too long building) " + s.print()); s.close(true); } else { //System.out.println("Checked "+diff+" "+s.print()); } } else { //System.out.println("OK "+diff+" "+s.print()); } } // check if circuit is establishing but doesn't had any action for a longer period of time if ((!c.established) && (!c.closed)) { if ((now_long - c.last_action.getTime()) / MILLISEC > (2*TorConfig.queueTimeoutCircuit)) { Logger.logGeneral(Logger.VERBOSE, "TorBackgroundMgmt.tear_down_closed_circuits(): closing (too long building) " + c.print()); c.close(false); } } // check if this circuit should not accept more streams if (c.established_streams > TorConfig.streamsPerCircuit) { Logger.logGeneral(Logger.VERBOSE, "TorBackgroundMgmt.tear_down_closed_circuits(): closing (maximum streams) " + c.print()); c.close(false); } // if closed, recall close() again and again to do garbage collection and stuff if (c.closed) c.close(false); // check if this circuit can be removed from the set of circuits if (c.destruct) { Logger.logGeneral(Logger.VERBOSE, "TorBackgroundMgmt.tear_down_closed_circuits(): destructing circuit " + c.print()); i2.remove(); } } } } public void close_all_circuits() { TorKeeper.setComplete(TorKeeper.CIRCUITS_AVAILABLE, false); Iterator<String> i = tor.fnh.tls.keySet().iterator(); while (i.hasNext()) { TLSConnection tls = (TLSConnection) tor.fnh.tls.get(i.next()); Iterator<Integer> i2 = tls.circuits.keySet().iterator(); while (i2.hasNext()) { Circuit c = (Circuit) tls.circuits.get(i2.next()); c.close(true); if (c.destruct) i2.remove(); } } this.interrupt(); } public void close() { // stop sub-thread dirman.stopped = true; dirman.interrupt(); // stop this thread this.stopped = true; this.interrupt(); } public void cleanup() { ListIterator<Thread> brtIterator = brtList.listIterator(); while (brtIterator.hasNext()) { Thread brt = brtIterator.next(); if (brt.isAlive()) brt.interrupt(); brtIterator.remove(); } } public void run() { try { sleep(INITIAL_INTERVAL * MILLISEC); } catch (InterruptedException e) { } // run until killed outerWhile: while (!stopped) { try { now_long = new Date().getTime(); // do work idle_circuits(); tear_down_closed_circuits(); send_keep_alive_packets(); // wait sleep(INTERVAL * MILLISEC); } catch (InterruptedException e) { break outerWhile; } catch (Exception e) { break outerWhile; } } cleanup(); } } /** * Directory-Manager Class. This class is done in a separate thread * to avoid stalling the other management tasks: updating a directory * can take quite an amount of time :-/ */ class DirectoryManager extends Thread { boolean stopped = false; private Tor tor; private long now_long; private long dir_next_update; // timestamp boolean fastStartup; DirectoryManager(Tor tor, boolean fastStartup) { this.fastStartup = fastStartup; this.tor = tor; dir_next_update = now_long; // + TorConfig.intervalDirectoryRefresh*60* MILLISEC; this.start(); } /** * keep up to date with the directory informations */ private void update_directory() { if ((now_long > dir_next_update) || (tor.dir.torServers.size()<1)) { Logger.logGeneral(Logger.INFO, "TorBackgroundMgmt.update_directory: updating directory"); if (fastStartup) this.setPriority(8); int updateVer = tor.dir.refreshListOfServers(fastStartup); if (fastStartup) fastStartup = false; if (updateVer==1) dir_next_update = now_long + TorConfig.intervalDirectoryV1Refresh* 60* TorBackgroundMgmt.MILLISEC; else dir_next_update = now_long + TorConfig.intervalDirectoryRefresh* 60* TorBackgroundMgmt.MILLISEC; if (tor.dir.torServers.size()>1) { //tor.dir.writeDirectoryToFile(TorConfig.getCacheFilename()); this.setPriority(3); // Lower priority for future updates } else { Logger.logGeneral(Logger.WARNING, "TorBackgroundMgmt.update_directory: no directory available"); } // Create a new KeyPair if needed tor.privateKeyHandler.prepareCachedKeyPairIfNeeded(); } } public void run() { // run until killed while (!stopped) { try { now_long = new Date().getTime(); // do work update_directory(); // wait sleep(TorBackgroundMgmt.INTERVAL * TorBackgroundMgmt.MILLISEC); } catch (Exception e) { stopped = true; } } } } // vim: et