diff -Bbur jtorctl/net/freehaven/tor/control/Bytes.java jtorctl-briar/net/freehaven/tor/control/Bytes.java --- jtorctl/net/freehaven/tor/control/Bytes.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/Bytes.java 2013-05-16 19:56:30.000000000 +0100 @@ -16,46 +16,43 @@ /** Write the two-byte value in 's' into the byte array 'ba', starting at * the index 'pos'. */ public static void setU16(byte[] ba, int pos, short s) { - ba[pos] = (byte)((s >> 8) & 0xff); - ba[pos+1] = (byte)((s ) & 0xff); + ba[pos] = (byte) ((s >> 8) & 0xff); + ba[pos + 1] = (byte) (s & 0xff); } /** Write the four-byte value in 'i' into the byte array 'ba', starting at * the index 'pos'. */ public static void setU32(byte[] ba, int pos, int i) { - ba[pos] = (byte)((i >> 24) & 0xff); - ba[pos+1] = (byte)((i >> 16) & 0xff); - ba[pos+2] = (byte)((i >> 8) & 0xff); - ba[pos+3] = (byte)((i ) & 0xff); + ba[pos] = (byte) ((i >> 24) & 0xff); + ba[pos + 1] = (byte) ((i >> 16) & 0xff); + ba[pos + 2] = (byte) ((i >> 8) & 0xff); + ba[pos + 3] = (byte) (i & 0xff); } /** Return the four-byte value starting at index 'pos' within 'ba' */ public static int getU32(byte[] ba, int pos) { - return - ((ba[pos ]&0xff)<<24) | - ((ba[pos+1]&0xff)<<16) | - ((ba[pos+2]&0xff)<< 8) | - ((ba[pos+3]&0xff)); + return ((ba[pos] & 0xff) << 24) | + ((ba[pos + 1] & 0xff) << 16) | + ((ba[pos + 2] & 0xff) << 8) | + ((ba[pos + 3] & 0xff)); } public static String getU32S(byte[] ba, int pos) { - return String.valueOf( (getU32(ba,pos))&0xffffffffL ); + return String.valueOf((getU32(ba, pos)) & 0xffffffffL); } /** Return the two-byte value starting at index 'pos' within 'ba' */ public static int getU16(byte[] ba, int pos) { - return - ((ba[pos ]&0xff)<<8) | - ((ba[pos+1]&0xff)); + return ((ba[pos] & 0xff) << 8) | + ((ba[pos + 1] & 0xff)); } /** Return the string starting at position 'pos' of ba and extending * until a zero byte or the end of the string. */ public static String getNulTerminatedStr(byte[] ba, int pos) { - int len, maxlen = ba.length-pos; - for (len=0; len<maxlen; ++len) { - if (ba[pos+len] == 0) - break; + int len, maxlen = ba.length - pos; + for(len = 0; len < maxlen; ++len) { + if(ba[pos + len] == 0) break; } return new String(ba, pos, len); } @@ -64,18 +61,16 @@ * Read bytes from 'ba' starting at 'pos', dividing them into strings * along the character in 'split' and writing them into 'lst' */ - public static void splitStr(List<String> lst, byte[] ba, int pos, byte split) { - while (pos < ba.length && ba[pos] != 0) { + public static void splitStr(List<String> lst, byte[] ba, int pos, + byte split) { + while(pos < ba.length && ba[pos] != 0) { int len; - for (len=0; pos+len < ba.length; ++len) { - if (ba[pos+len] == 0 || ba[pos+len] == split) - break; + for(len = 0; pos + len < ba.length; ++len) { + if(ba[pos + len] == 0 || ba[pos + len] == split) break; } - if (len>0) - lst.add(new String(ba, pos, len)); + if(len > 0) lst.add(new String(ba, pos, len)); pos += len; - if (ba[pos] == split) - ++pos; + if(ba[pos] == split) ++pos; } } @@ -86,11 +81,8 @@ public static List<String> splitStr(List<String> lst, String str) { // split string on spaces, include trailing/leading String[] tokenArray = str.split(" ", -1); - if (lst == null) { - lst = Arrays.asList( tokenArray ); - } else { - lst.addAll( Arrays.asList( tokenArray ) ); - } + if(lst == null) lst = Arrays.asList(tokenArray); + else lst.addAll(Arrays.asList(tokenArray)); return lst; } @@ -101,10 +93,10 @@ public static final String hex(byte[] ba) { StringBuffer buf = new StringBuffer(); - for (int i = 0; i < ba.length; ++i) { - int b = (ba[i]) & 0xff; + for(int i = 0; i < ba.length; ++i) { + int b = ba[i] & 0xff; buf.append(NYBBLES[b >> 4]); - buf.append(NYBBLES[b&0x0f]); + buf.append(NYBBLES[b & 0x0f]); } return buf.toString(); } diff -Bbur jtorctl/net/freehaven/tor/control/ConfigEntry.java jtorctl-briar/net/freehaven/tor/control/ConfigEntry.java --- jtorctl/net/freehaven/tor/control/ConfigEntry.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/ConfigEntry.java 2013-05-16 19:56:30.000000000 +0100 @@ -4,6 +4,11 @@ /** A single key-value pair from Tor's configuration. */ public class ConfigEntry { + + public final String key; + public final String value; + public final boolean is_default; + public ConfigEntry(String k, String v) { key = k; value = v; @@ -14,7 +20,4 @@ value = ""; is_default = true; } - public final String key; - public final String value; - public final boolean is_default; } diff -Bbur jtorctl/net/freehaven/tor/control/EventHandler.java jtorctl-briar/net/freehaven/tor/control/EventHandler.java --- jtorctl/net/freehaven/tor/control/EventHandler.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/EventHandler.java 2013-05-16 19:56:30.000000000 +0100 @@ -9,9 +9,10 @@ * @see TorControlConnection#setEvents */ public interface EventHandler { + /** - * Invoked when a circuit's status has changed. - * Possible values for <b>status</b> are: + * Invoked when a circuit's status has changed. Possible values for + * <b>status</b> are: * <ul> * <li>"LAUNCHED" : circuit ID assigned to new circuit</li> * <li>"BUILT" : all hops finished, can now accept streams</li> @@ -19,7 +20,6 @@ * <li>"FAILED" : circuit closed (was not built)</li> * <li>"CLOSED" : circuit closed (was built)</li> * </ul> - * * <b>circID</b> is the alphanumeric identifier of the affected circuit, * and <b>path</b> is a comma-separated list of alphanumeric ServerIDs. */ @@ -24,9 +24,10 @@ * and <b>path</b> is a comma-separated list of alphanumeric ServerIDs. */ public void circuitStatus(String status, String circID, String path); + /** - * Invoked when a stream's status has changed. - * Possible values for <b>status</b> are: + * Invoked when a stream's status has changed. Possible values for + * <b>status</b> are: * <ul> * <li>"NEW" : New request to connect</li> * <li>"NEWRESOLVE" : New request to resolve an address</li> @@ -37,7 +38,6 @@ * <li>"CLOSED" : Stream closed</li> * <li>"DETACHED" : Detached from circuit; still retriable.</li> * </ul> - * * <b>streamID</b> is the alphanumeric identifier of the affected stream, * and its <b>target</b> is specified as address:port. */ @@ -42,18 +42,21 @@ * and its <b>target</b> is specified as address:port. */ public void streamStatus(String status, String streamID, String target); + /** - * Invoked when the status of a connection to an OR has changed. - * Possible values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | "CLOSED"]. - * <b>orName</b> is the alphanumeric identifier of the OR affected. + * Invoked when the status of a connection to an OR has changed. Possible + * values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | + * "CLOSED"]. <b>orName</b> is the alphanumeric identifier of the OR + * affected. */ public void orConnStatus(String status, String orName); + /** - * Invoked once per second. <b>read</b> and <b>written</b> are - * the number of bytes read and written, respectively, in - * the last second. + * Invoked once per second. <b>read</b> and <b>written</b> are the number + * of bytes read and written, respectively, in the last second. */ public void bandwidthUsed(long read, long written); + /** * Invoked whenever Tor learns about new ORs. The <b>orList</b> object * contains the alphanumeric ServerIDs associated with the new ORs. @@ -59,17 +62,18 @@ * contains the alphanumeric ServerIDs associated with the new ORs. */ public void newDescriptors(java.util.List<String> orList); + /** - * Invoked when Tor logs a message. - * <b>severity</b> is one of ["DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERR"], - * and <b>msg</b> is the message string. + * Invoked when Tor logs a message. <b>severity</b> is one of ["DEBUG" | + * "INFO" | "NOTICE" | "WARN" | "ERR"], and <b>msg</b> is the message + * string. */ public void message(String severity, String msg); + /** - * Invoked when an unspecified message is received. - * <type> is the message type, and <msg> is the message string. + * Invoked when an unspecified message is received. <type> is the message + * type, and <msg> is the message string. */ public void unrecognized(String type, String msg); - } diff -Bbur jtorctl/net/freehaven/tor/control/examples/DebuggingEventHandler.java jtorctl-briar/net/freehaven/tor/control/examples/DebuggingEventHandler.java --- jtorctl/net/freehaven/tor/control/examples/DebuggingEventHandler.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/examples/DebuggingEventHandler.java 2013-05-16 19:56:30.000000000 +0100 @@ -3,42 +3,48 @@ package net.freehaven.tor.control.examples; import java.io.PrintWriter; -import java.util.Iterator; +import java.util.List; + import net.freehaven.tor.control.EventHandler; public class DebuggingEventHandler implements EventHandler { - protected PrintWriter out; + private final PrintWriter out; - public DebuggingEventHandler(PrintWriter p) { - out = p; + public DebuggingEventHandler(PrintWriter out) { + this.out = out; } public void circuitStatus(String status, String circID, String path) { - out.println("Circuit "+circID+" is now "+status+" (path="+path+")"); + out.println("Circuit " + circID + " is now " + status + + " (path=" + path + ")"); } + public void streamStatus(String status, String streamID, String target) { - out.println("Stream "+streamID+" is now "+status+" (target="+target+")"); + out.println("Stream " + streamID + " is now " + status + + " (target=" + target + ")"); } + public void orConnStatus(String status, String orName) { - out.println("OR connection to "+orName+" is now "+status); + out.println("OR connection to " + orName + " is now " + status); } + public void bandwidthUsed(long read, long written) { - out.println("Bandwidth usage: "+read+" bytes read; "+ - written+" bytes written."); + out.println("Bandwidth usage: " + read + " bytes read; " + + written +" bytes written."); } - public void newDescriptors(java.util.List<String> orList) { + + public void newDescriptors(List<String> orList) { out.println("New descriptors for routers:"); - for (Iterator<String> i = orList.iterator(); i.hasNext(); ) - out.println(" "+i.next()); + for(String or : orList) out.println(" " + or); } + public void message(String type, String msg) { - out.println("["+type+"] "+msg.trim()); + out.println("[" + type + "] " + msg.trim()); } public void unrecognized(String type, String msg) { - out.println("unrecognized event ["+type+"] "+msg.trim()); + out.println("unrecognized event [" + type + "] " + msg.trim()); } - } diff -Bbur jtorctl/net/freehaven/tor/control/examples/Main.java jtorctl-briar/net/freehaven/tor/control/examples/Main.java --- jtorctl/net/freehaven/tor/control/examples/Main.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/examples/Main.java 2013-05-16 19:56:30.000000000 +0100 @@ -2,59 +2,60 @@ // See LICENSE file for copying information package net.freehaven.tor.control.examples; -import net.freehaven.tor.control.*; -import java.io.PrintWriter; +import java.io.EOFException; import java.io.IOException; +import java.io.PrintWriter; +import java.net.Socket; import java.util.ArrayList; -import java.util.List; import java.util.Arrays; +import java.util.List; import java.util.Map; -import java.util.Iterator; + +import net.freehaven.tor.control.ConfigEntry; +import net.freehaven.tor.control.PasswordDigest; +import net.freehaven.tor.control.TorControlCommands; +import net.freehaven.tor.control.TorControlConnection; +import net.freehaven.tor.control.TorControlError; public class Main implements TorControlCommands { public static void main(String args[]) { - if (args.length < 1) { + if(args.length < 1) { System.err.println("No command given."); return; } try { - if (args[0].equals("set-config")) { + if(args[0].equals("set-config")) { setConfig(args); - } else if (args[0].equals("get-config")) { + } else if(args[0].equals("get-config")) { getConfig(args); - } else if (args[0].equals("get-info")) { + } else if(args[0].equals("get-info")) { getInfo(args); - } else if (args[0].equals("listen")) { + } else if(args[0].equals("listen")) { listenForEvents(args); - } else if (args[0].equals("signal")) { + } else if(args[0].equals("signal")) { signal(args); - } else if (args[0].equals("auth")) { + } else if(args[0].equals("auth")) { authDemo(args); } else { System.err.println("Unrecognized command: "+args[0]); } - } catch (java.io.EOFException ex) { + } catch(EOFException ex) { System.out.println("Control socket closed by Tor."); - } catch (IOException ex) { - System.err.println("IO exception when talking to Tor process: "+ + } catch(TorControlError ex) { + System.err.println("Error from Tor process: " + + ex + " [" + ex.getErrorMsg() + "]"); + } catch(IOException ex) { + System.err.println("IO exception when talking to Tor process: " + ex); ex.printStackTrace(System.err); - } catch (TorControlError ex) { - System.err.println("Error from Tor process: "+ - ex+" ["+ex.getErrorMsg()+"]"); } } private static TorControlConnection getConnection(String[] args, - boolean daemon) - throws IOException { - TorControlConnection conn = TorControlConnection.getConnection( - new java.net.Socket("127.0.0.1", 9100)); - //if (conn instanceof TorControlConnection1) { - // System.err.println("Debugging"); - // ((TorControlConnection1)conn).setDebugging(System.err); - //} + boolean daemon) throws IOException { + Socket s = new Socket("127.0.0.1", 9100); + TorControlConnection conn = new TorControlConnection(s); conn.launchThread(daemon); conn.authenticate(new byte[0]); return conn; @@ -71,57 +72,52 @@ ArrayList<String> lst = new ArrayList<String>(); int i = 1; boolean save = false; - if (args[i].equals("-save")) { + if(args[i].equals("-save")) { save = true; ++i; } - for (; i < args.length; i +=2) { - lst.add(args[i]+" "+args[i+1]); + for(; i < args.length; i +=2) { + lst.add(args[i] + " " + args[i + 1]); } conn.setConf(lst); - if (save) { - conn.saveConf(); - } + if(save) conn.saveConf(); } public static void getConfig(String[] args) throws IOException { // Usage: get-config key key key TorControlConnection conn = getConnection(args); - List<ConfigEntry> lst = conn.getConf(Arrays.asList(args).subList(1,args.length)); - for (Iterator<ConfigEntry> i = lst.iterator(); i.hasNext(); ) { - ConfigEntry e = i.next(); - System.out.println("KEY: "+e.key); - System.out.println("VAL: "+e.value); + List<String> keys = Arrays.asList(args).subList(1, args.length); + List<ConfigEntry> lst = conn.getConf(keys); + for(ConfigEntry e : lst) { + System.out.println("KEY: " + e.key); + System.out.println("VAL: " + e.value); } } public static void getInfo(String[] args) throws IOException { TorControlConnection conn = getConnection(args); - Map<String,String> m = conn.getInfo(Arrays.asList(args).subList(1,args.length)); - for (Iterator<Map.Entry<String, String>> i = m.entrySet().iterator(); i.hasNext(); ) { - Map.Entry<String,String> e = i.next(); - System.out.println("KEY: "+e.getKey()); - System.out.println("VAL: "+e.getValue()); + List<String> keys = Arrays.asList(args).subList(1, args.length); + Map<String, String> m = conn.getInfo(keys); + for(Map.Entry<String, String> e : m.entrySet()) { + System.out.println("KEY: " + e.getKey()); + System.out.println("VAL: " + e.getValue()); } } public static void listenForEvents(String[] args) throws IOException { // Usage: listen [circ|stream|orconn|bw|newdesc|info|notice|warn|error]* TorControlConnection conn = getConnection(args, false); - ArrayList<String> lst = new ArrayList<String>(); - for (int i = 1; i < args.length; ++i) { - lst.add(args[i]); - } + List<String> events = Arrays.asList(args).subList(1, args.length); conn.setEventHandler( new DebuggingEventHandler(new PrintWriter(System.out, true))); - conn.setEvents(lst); + conn.setEvents(events); } public static void signal(String[] args) throws IOException { // Usage signal [reload|shutdown|dump|debug|halt] TorControlConnection conn = getConnection(args, false); // distinguish shutdown signal from other signals - if ("SHUTDOWN".equalsIgnoreCase(args[1]) + if("SHUTDOWN".equalsIgnoreCase(args[1]) || "HALT".equalsIgnoreCase(args[1])) { conn.shutdownTor(args[1].toUpperCase()); } else { @@ -130,17 +126,13 @@ } public static void authDemo(String[] args) throws IOException { - PasswordDigest pwd = PasswordDigest.generateDigest(); - java.net.Socket s = new java.net.Socket("127.0.0.1", 9100); - TorControlConnection conn = TorControlConnection.getConnection(s); + Socket s = new Socket("127.0.0.1", 9100); + TorControlConnection conn = new TorControlConnection(s); conn.launchThread(true); conn.authenticate(new byte[0]); - conn.setConf("HashedControlPassword", pwd.getHashedPassword()); - - conn = TorControlConnection.getConnection( - new java.net.Socket("127.0.0.1", 9100)); + conn = new TorControlConnection(new Socket("127.0.0.1", 9100)); conn.launchThread(true); conn.authenticate(pwd.getSecret()); } diff -Bbur jtorctl/net/freehaven/tor/control/PasswordDigest.java jtorctl-briar/net/freehaven/tor/control/PasswordDigest.java --- jtorctl/net/freehaven/tor/control/PasswordDigest.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/PasswordDigest.java 2013-05-16 19:56:30.000000000 +0100 @@ -2,19 +2,20 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.MessageDigest; /** - * A hashed digest of a secret password (used to set control connection + * A hashed digest of a secret password(used to set control connection * security.) * * For the actual hashing algorithm, see RFC2440's secret-to-key conversion. */ public class PasswordDigest { - byte[] secret; - String hashedKey; + private final byte[] secret; + private final String hashedKey; /** Return a new password digest with a random secret and salt. */ public static PasswordDigest generateDigest() { @@ -35,17 +36,16 @@ */ public PasswordDigest(byte[] secret, byte[] specifier) { this.secret = secret.clone(); - if (specifier == null) { + if(specifier == null) { specifier = new byte[9]; SecureRandom rng = new SecureRandom(); rng.nextBytes(specifier); specifier[8] = 96; } - hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier)); + hashedKey = "16:" + Bytes.hex(secretToKey(secret, specifier)); } - /** Return the secret used to generate this password hash. - */ + /** Return the secret used to generate this password hash. */ public byte[] getSecret() { return secret.clone(); } @@ -63,17 +63,17 @@ MessageDigest d; try { d = MessageDigest.getInstance("SHA-1"); - } catch (java.security.NoSuchAlgorithmException ex) { + } catch(NoSuchAlgorithmException ex) { throw new RuntimeException("Can't run without sha-1."); } - int c = (specifier[8])&0xff; - int count = (16 + (c&15)) << ((c>>4) + EXPBIAS); + int c = specifier[8] & 0xff; + int count = (16 + (c & 15)) << ((c >> 4) + EXPBIAS); - byte[] tmp = new byte[8+secret.length]; + byte[] tmp = new byte[8 + secret.length]; System.arraycopy(specifier, 0, tmp, 0, 8); System.arraycopy(secret, 0, tmp, 8, secret.length); - while (count > 0) { - if (count >= tmp.length) { + while(count > 0) { + if(count >= tmp.length) { d.update(tmp); count -= tmp.length; } else { @@ -81,17 +81,9 @@ count = 0; } } - byte[] key = new byte[20+9]; + byte[] key = new byte[20 + 9]; System.arraycopy(d.digest(), 0, key, 9, 20); System.arraycopy(specifier, 0, key, 0, 9); return key; } - - /** Return a hexadecimal encoding of a byte array. */ - // XXX There must be a better way to do this in Java. - private static final String encodeBytes(byte[] ba) { - return Bytes.hex(ba); - } - } - diff -Bbur jtorctl/net/freehaven/tor/control/TorControlCommands.java jtorctl-briar/net/freehaven/tor/control/TorControlCommands.java --- jtorctl/net/freehaven/tor/control/TorControlCommands.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlCommands.java 2013-05-16 19:56:30.000000000 +0100 @@ -119,7 +119,10 @@ public static final byte OR_CONN_STATUS_CLOSED = 0x03; public static final String[] OR_CONN_STATUS_NAMES = { - "LAUNCHED","CONNECTED","FAILED","CLOSED" + "LAUNCHED", + "CONNECTED", + "FAILED", + "CLOSED" }; public static final byte SIGNAL_HUP = 0x01; diff -Bbur jtorctl/net/freehaven/tor/control/TorControlConnection.java jtorctl-briar/net/freehaven/tor/control/TorControlConnection.java --- jtorctl/net/freehaven/tor/control/TorControlConnection.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlConnection.java 2013-05-16 19:56:30.000000000 +0100 @@ -2,120 +2,107 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.io.BufferedReader; import java.io.IOException; -import java.net.SocketException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.Socket; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; -import java.util.concurrent.CancellationException; /** A connection to a running Tor process as specified in control-spec.txt. */ -public class TorControlConnection implements TorControlCommands -{ +public class TorControlConnection implements TorControlCommands { - protected EventHandler handler; + private final LinkedList<Waiter> waiters; + private final BufferedReader input; + private final Writer output; - protected LinkedList<Waiter> waiters; + private ControlParseThread thread; // Locking: this - protected ControlParseThread thread; + private volatile EventHandler handler; + private volatile PrintWriter debugOutput; + private volatile IOException parseThreadException; - protected java.io.BufferedReader input; + private static class Waiter { - protected java.io.Writer output; - - protected java.io.PrintWriter debugOutput; - - static class Waiter { List<ReplyLine> response; - public synchronized List<ReplyLine> getResponse() { - try { - while (response == null) { - wait(); - } - } catch (InterruptedException ex) { - throw new CancellationException( - "Please don't interrupt library calls."); - } + + synchronized List<ReplyLine> getResponse() throws InterruptedException { + while(response == null) wait(); return response; } - public synchronized void setResponse(List<ReplyLine> response) { + + synchronized void setResponse(List<ReplyLine> response) { this.response = response; notifyAll(); } } - static class ReplyLine { - public String status; - public String msg; - public String rest; + private static class ReplyLine { + + final String status; + final String msg; + final String rest; ReplyLine(String status, String msg, String rest) { - this.status = status; this.msg = msg; this.rest = rest; - } + this.status = status; + this.msg = msg; + this.rest = rest; } - - public static TorControlConnection getConnection(java.net.Socket sock) - throws IOException - { - return new TorControlConnection(sock); } /** Create a new TorControlConnection to communicate with Tor over * a given socket. After calling this constructor, it is typical to * call launchThread and authenticate. */ - public TorControlConnection(java.net.Socket connection) - throws IOException { - this(connection.getInputStream(), connection.getOutputStream()); + public TorControlConnection(Socket s) throws IOException { + this(s.getInputStream(), s.getOutputStream()); } /** Create a new TorControlConnection to communicate with Tor over * an arbitrary pair of data streams. */ - public TorControlConnection(java.io.InputStream i, java.io.OutputStream o) { - this(new java.io.InputStreamReader(i), - new java.io.OutputStreamWriter(o)); + public TorControlConnection(InputStream i, OutputStream o) { + this(new InputStreamReader(i), new OutputStreamWriter(o)); } - public TorControlConnection(java.io.Reader i, java.io.Writer o) { - this.output = o; - if (i instanceof java.io.BufferedReader) - this.input = (java.io.BufferedReader) i; - else - this.input = new java.io.BufferedReader(i); - - this.waiters = new LinkedList<Waiter>(); + public TorControlConnection(Reader i, Writer o) { + if(i instanceof BufferedReader) input = (BufferedReader) i; + else input = new BufferedReader(i); + output = o; + waiters = new LinkedList<Waiter>(); } - protected final void writeEscaped(String s) throws IOException { + private void writeEscaped(String s) throws IOException { StringTokenizer st = new StringTokenizer(s, "\n"); - while (st.hasMoreTokens()) { + while(st.hasMoreTokens()) { String line = st.nextToken(); - if (line.startsWith(".")) - line = "."+line; - if (line.endsWith("\r")) - line += "\n"; - else - line += "\r\n"; - if (debugOutput != null) - debugOutput.print(">> "+line); + if(line.startsWith(".")) line = "." + line; + if(line.endsWith("\r")) line += "\n"; + else line += "\r\n"; + if(debugOutput != null) debugOutput.print(">> " + line); output.write(line); } output.write(".\r\n"); - if (debugOutput != null) - debugOutput.print(">> .\n"); + if(debugOutput != null) debugOutput.print(">> .\n"); } - protected static final String quote(String s) { + private static final String quote(String s) { StringBuffer sb = new StringBuffer("\""); - for (int i = 0; i < s.length(); ++i) { + for(int i = 0; i < s.length(); ++i) { char c = s.charAt(i); - switch (c) - { + switch (c) { case '\r': case '\n': case '\\': @@ -128,15 +115,15 @@ return sb.toString(); } - protected final ArrayList<ReplyLine> readReply() throws IOException { + private ArrayList<ReplyLine> readReply() throws IOException { ArrayList<ReplyLine> reply = new ArrayList<ReplyLine>(); char c; do { String line = input.readLine(); - if (line == null) { + if(line == null) { // if line is null, the end of the stream has been reached, i.e. // the connection to Tor has been closed! - if (reply.isEmpty()) { + if(reply.isEmpty()) { // nothing received so far, can exit cleanly return reply; } @@ -144,91 +131,86 @@ throw new TorControlSyntaxError("Connection to Tor " + " broke down while receiving reply!"); } - if (debugOutput != null) - debugOutput.println("<< "+line); - if (line.length() < 4) - throw new TorControlSyntaxError("Line (\""+line+"\") too short"); - String status = line.substring(0,3); + if(debugOutput != null) debugOutput.println("<< " + line); + if(line.length() < 4) { + throw new TorControlSyntaxError("Line (\"" + line + + "\") too short"); + } + String status = line.substring(0, 3); c = line.charAt(3); String msg = line.substring(4); String rest = null; - if (c == '+') { + if(c == '+') { StringBuffer data = new StringBuffer(); - while (true) { + while(true) { line = input.readLine(); - if (debugOutput != null) - debugOutput.print("<< "+line); - if (line.equals(".")) - break; - else if (line.startsWith(".")) - line = line.substring(1); + if(debugOutput != null) debugOutput.print("<< " + line); + if(line.equals(".")) break; + if(line.startsWith(".")) line = line.substring(1); data.append(line).append('\n'); } rest = data.toString(); } reply.add(new ReplyLine(status, msg, rest)); - } while (c != ' '); - + } while(c != ' '); return reply; } - protected synchronized List<ReplyLine> sendAndWaitForResponse(String s,String rest) - throws IOException { + private synchronized List<ReplyLine> sendAndWaitForResponse(String s, + String rest) throws IOException { + if(parseThreadException != null) throw parseThreadException; checkThread(); Waiter w = new Waiter(); - if (debugOutput != null) - debugOutput.print(">> "+s); - synchronized (waiters) { + if(debugOutput != null) debugOutput.print(">> " + s); + synchronized(waiters) { output.write(s); - if (rest != null) - writeEscaped(rest); + if(rest != null) writeEscaped(rest); output.flush(); waiters.addLast(w); } - List<ReplyLine> lst = w.getResponse(); - for (Iterator<ReplyLine> i = lst.iterator(); i.hasNext(); ) { - ReplyLine c = i.next(); - if (! c.status.startsWith("2")) - throw new TorControlError("Error reply: "+c.msg); + List<ReplyLine> lst; + try { + lst = w.getResponse(); + } catch(InterruptedException ex) { + throw new IOException(ex); + } + for(ReplyLine line : lst) { + if(!line.status.startsWith("2")) + throw new TorControlError("Error reply: " + line.msg); } return lst; } /** Helper: decode a CMD_EVENT command and dispatch it to our * EventHandler (if any). */ - protected void handleEvent(ArrayList<ReplyLine> events) { - if (handler == null) - return; - - for (Iterator<ReplyLine> i = events.iterator(); i.hasNext(); ) { - ReplyLine line = i.next(); + private void handleEvent(ArrayList<ReplyLine> events) { + if(handler == null) return; + for(ReplyLine line : events) { int idx = line.msg.indexOf(' '); String tp = line.msg.substring(0, idx).toUpperCase(); String rest = line.msg.substring(idx+1); - if (tp.equals("CIRC")) { + if(tp.equals("CIRC")) { List<String> lst = Bytes.splitStr(null, rest); - handler.circuitStatus(lst.get(1), - lst.get(0), - lst.get(1).equals("LAUNCHED") - || lst.size() < 2 ? "" - : lst.get(2)); - } else if (tp.equals("STREAM")) { + String path; + if(lst.get(1).equals("LAUNCHED") || lst.size() < 2) path = ""; + else path = lst.get(2); + handler.circuitStatus(lst.get(1), lst.get(0), path); + } else if(tp.equals("STREAM")) { List<String> lst = Bytes.splitStr(null, rest); - handler.streamStatus(lst.get(1), - lst.get(0), - lst.get(3)); + handler.streamStatus(lst.get(1), lst.get(0), lst.get(3)); // XXXX circID. - } else if (tp.equals("ORCONN")) { + } else if(tp.equals("ORCONN")) { List<String> lst = Bytes.splitStr(null, rest); handler.orConnStatus(lst.get(1), lst.get(0)); - } else if (tp.equals("BW")) { + } else if(tp.equals("BW")) { List<String> lst = Bytes.splitStr(null, rest); - handler.bandwidthUsed(Integer.parseInt(lst.get(0)), - Integer.parseInt(lst.get(1))); - } else if (tp.equals("NEWDESC")) { + int read = Integer.parseInt(lst.get(0)); + int written = Integer.parseInt(lst.get(1)); + handler.bandwidthUsed(read, written); + } else if(tp.equals("NEWDESC")) { List<String> lst = Bytes.splitStr(null, rest); handler.newDescriptors(lst); - } else if (tp.equals("DEBUG") || + } else if(tp.equals("DEBUG") || tp.equals("INFO") || tp.equals("NOTICE") || tp.equals("WARN") || @@ -240,23 +222,22 @@ } } - /** Sets <b>w</b> as the PrintWriter for debugging output, * which writes out all messages passed between Tor and the controller. - * Outgoing messages are preceded by "\>\>" and incoming messages are preceded - * by "\<\<" + * Outgoing messages are preceded by "\>\>" and incoming messages are + * preceded by "\<\<" */ - public void setDebugging(java.io.PrintWriter w) { + public void setDebugging(PrintWriter w) { debugOutput = w; } /** Sets <b>s</b> as the PrintStream for debugging output, * which writes out all messages passed between Tor and the controller. - * Outgoing messages are preceded by "\>\>" and incoming messages are preceded - * by "\<\<" + * Outgoing messages are preceded by "\>\>" and incoming messages are + * preceded by "\<\<" */ - public void setDebugging(java.io.PrintStream s) { - debugOutput = new java.io.PrintWriter(s, true); + public void setDebugging(PrintStream s) { + debugOutput = new PrintWriter(s, true); } /** Set the EventHandler object that will be notified of any @@ -271,52 +252,43 @@ * This is necessary to handle asynchronous events and synchronous * responses that arrive independantly over the same socket. */ - public Thread launchThread(boolean daemon) { + public synchronized Thread launchThread(boolean daemon) { ControlParseThread th = new ControlParseThread(); - if (daemon) - th.setDaemon(true); + if(daemon) th.setDaemon(true); th.start(); - this.thread = th; + thread = th; return th; } - protected class ControlParseThread extends Thread { - boolean stopped = false; + private class ControlParseThread extends Thread { + @Override public void run() { try { react(); - } catch (SocketException ex) { - if (stopped) // we expected this exception - return; - throw new RuntimeException(ex); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + } catch(IOException ex) { + parseThreadException = ex; } - public void stopListening() { - this.stopped = true; } } - protected final void checkThread() { - if (thread == null) - launchThread(true); + private synchronized void checkThread() { + if(thread == null) launchThread(true); } /** helper: implement the main background loop. */ - protected void react() throws IOException { - while (true) { + private void react() throws IOException { + while(true) { ArrayList<ReplyLine> lst = readReply(); - if (lst.isEmpty()) { + if(lst.isEmpty()) { // connection has been closed remotely! end the loop! return; } - if ((lst.get(0)).status.startsWith("6")) + if((lst.get(0)).status.startsWith("6")) { handleEvent(lst); - else { + } else { Waiter w; - synchronized (waiters) { + synchronized(waiters) { w = waiters.removeFirst(); } w.setResponse(lst); @@ -327,17 +299,14 @@ /** Change the value of the configuration option 'key' to 'val'. */ public void setConf(String key, String value) throws IOException { - List<String> lst = new ArrayList<String>(); - lst.add(key+" "+value); - setConf(lst); + setConf(Arrays.asList(key + " " + value)); } /** Change the values of the configuration options stored in kvMap. */ public void setConf(Map<String, String> kvMap) throws IOException { List<String> lst = new ArrayList<String>(); - for (Iterator<Map.Entry<String,String>> it = kvMap.entrySet().iterator(); it.hasNext(); ) { - Map.Entry<String,String> ent = it.next(); - lst.add(ent.getKey()+" "+ent.getValue()+"\n"); + for(Map.Entry<String, String> e : kvMap.entrySet()) { + lst.add(e.getKey() + " " + e.getValue() + "\n"); } setConf(lst); } @@ -345,34 +314,35 @@ /** Changes the values of the configuration options stored in * <b>kvList</b>. Each list element in <b>kvList</b> is expected to be * String of the format "key value". - * + * <p> * Tor behaves as though it had just read each of the key-value pairs * from its configuration file. Keywords with no corresponding values have * their configuration values reset to their defaults. setConf is - * all-or-nothing: if there is an error in any of the configuration settings, - * Tor sets none of them. - * + * all-or-nothing: if there is an error in any of the configuration + * settings, Tor sets none of them. + * <p> * When a configuration option takes multiple values, or when multiple - * configuration keys form a context-sensitive group (see getConf below), then - * setting any of the options in a setConf command is taken to reset all of - * the others. For example, if two ORBindAddress values are configured, and a - * command arrives containing a single ORBindAddress value, the new - * command's value replaces the two old values. - * + * configuration keys form a context-sensitive group (see getConf below), + * then setting any of the options in a setConf command is taken to reset + * all of the others. For example, if two ORBindAddress values are + * configured, and a command arrives containing a single ORBindAddress + * value, the new command's value replaces the two old values. + * <p> * To remove all settings for a given option entirely (and go back to its - * default value), include a String in <b>kvList</b> containing the key and no value. + * default value), include a String in <b>kvList</b> containing the key and + * no value. */ public void setConf(Collection<String> kvList) throws IOException { - if (kvList.size() == 0) - return; + if(kvList.size() == 0) return; StringBuffer b = new StringBuffer("SETCONF"); - for (Iterator<String> it = kvList.iterator(); it.hasNext(); ) { - String kv = it.next(); - int i = kv.indexOf(' '); - if (i == -1) + for(String kv : kvList) { + int idx = kv.indexOf(' '); + if(idx == -1) { b.append(" ").append(kv); - b.append(" ").append(kv.substring(0,i)).append("=") - .append(quote(kv.substring(i+1))); + } else { + b.append(" ").append(kv.substring(0, idx)); + b.append("=").append(quote(kv.substring(idx + 1))); + } } b.append("\r\n"); sendAndWaitForResponse(b.toString(), null); @@ -382,11 +352,9 @@ * default values. **/ public void resetConf(Collection<String> keys) throws IOException { - if (keys.size() == 0) - return; + if(keys.size() == 0) return; StringBuffer b = new StringBuffer("RESETCONF"); - for (Iterator<String> it = keys.iterator(); it.hasNext(); ) { - String key = it.next(); + for(String key : keys) { b.append(" ").append(key); } b.append("\r\n"); @@ -400,36 +368,38 @@ return getConf(lst); } - /** Requests the values of the configuration variables listed in <b>keys</b>. - * Results are returned as a list of ConfigEntry objects. - * + /** Requests the values of the configuration variables listed in + * <b>keys</b>. Results are returned as a list of ConfigEntry objects. + * <p> * If an option appears multiple times in the configuration, all of its * key-value pairs are returned in order. - * + * <p> * Some options are context-sensitive, and depend on other options with * different keywords. These cannot be fetched directly. Currently there * is only one such option: clients should use the "HiddenServiceOptions" * virtual keyword to get all HiddenServiceDir, HiddenServicePort, * HiddenServiceNodes, and HiddenServiceExcludeNodes option settings. */ - public List<ConfigEntry> getConf(Collection<String> keys) throws IOException { + public List<ConfigEntry> getConf(Collection<String> keys) + throws IOException { StringBuffer sb = new StringBuffer("GETCONF"); - for (Iterator<String> it = keys.iterator(); it.hasNext(); ) { - String key = it.next(); + for(String key : keys) { sb.append(" ").append(key); } sb.append("\r\n"); List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null); List<ConfigEntry> result = new ArrayList<ConfigEntry>(); - for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) { - String kv = (it.next()).msg; + for(ReplyLine line : lst) { + String kv = line.msg; int idx = kv.indexOf('='); - if (idx >= 0) - result.add(new ConfigEntry(kv.substring(0, idx), - kv.substring(idx+1))); - else + if(idx >= 0) { + String key = kv.substring(0, idx); + String value = kv.substring(idx + 1); + result.add(new ConfigEntry(key, value)); + } else { result.add(new ConfigEntry(kv)); } + } return result; } @@ -437,37 +407,41 @@ * Each element of <b>events</b> is one of the following Strings: * ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" | * "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] . - * + * <p> * Any events not listed in the <b>events</b> are turned off; thus, calling - * setEvents with an empty <b>events</b> argument turns off all event reporting. + * setEvents with an empty <b>events</b> argument turns off all event + * reporting. */ public void setEvents(List<String> events) throws IOException { StringBuffer sb = new StringBuffer("SETEVENTS"); - for (Iterator<String> it = events.iterator(); it.hasNext(); ) { - sb.append(" ").append(it.next()); + for(String event : events) { + sb.append(" ").append(event); } sb.append("\r\n"); sendAndWaitForResponse(sb.toString(), null); } /** Authenticates the controller to the Tor server. - * + * <p> * By default, the current Tor implementation trusts all local users, and - * the controller can authenticate itself by calling authenticate(new byte[0]). - * - * If the 'CookieAuthentication' option is true, Tor writes a "magic cookie" - * file named "control_auth_cookie" into its data directory. To authenticate, - * the controller must send the contents of this file in <b>auth</b>. - * - * If the 'HashedControlPassword' option is set, <b>auth</b> must contain the salted - * hash of a secret password. The salted hash is computed according to the - * S2K algorithm in RFC 2440 (OpenPGP), and prefixed with the s2k specifier. - * This is then encoded in hexadecimal, prefixed by the indicator sequence - * "16:". - * + * the controller can authenticate itself by calling + * authenticate(new byte[0]). + * <p> + * If the 'CookieAuthentication' option is true, Tor writes a "magic + * cookie" file named "control_auth_cookie" into its data directory. To + * authenticate, the controller must send the contents of this file in + * <b>auth</b>. + * <p> + * If the 'HashedControlPassword' option is set, <b>auth</b> must contain + * the salted hash of a secret password. The salted hash is computed + * according to the S2K algorithm in RFC 2440 (OpenPGP), and prefixed with + * the s2k specifier. This is then encoded in hexadecimal, prefixed by the + * indicator sequence "16:". + * <p> * You can generate the salt of a password by calling - * 'tor --hash-password <password>' + * <tt>'tor --hash-password <password>'</tt> * or by using the provided PasswordDigest class. + * <p> * To authenticate under this scheme, the controller sends Tor the original * secret that was used to generate the password. */ @@ -476,7 +450,8 @@ sendAndWaitForResponse(cmd, null); } - /** Instructs the server to write out its configuration options into its torrc. + /** Instructs the server to write out its configuration options into its + * torrc. */ public void saveConf() throws IOException { sendAndWaitForResponse("SAVECONF\r\n", null); @@ -503,234 +478,239 @@ public void shutdownTor(String signal) throws IOException { String s = "SIGNAL " + signal + "\r\n"; Waiter w = new Waiter(); - if (debugOutput != null) - debugOutput.print(">> "+s); - if (this.thread != null) { - this.thread.stopListening(); - } - synchronized (waiters) { + if(debugOutput != null) debugOutput.print(">> " + s); + synchronized(waiters) { output.write(s); output.flush(); waiters.addLast(w); // Prevent react() from finding the list empty } } - /** Tells the Tor server that future SOCKS requests for connections to a set of original - * addresses should be replaced with connections to the specified replacement - * addresses. Each element of <b>kvLines</b> is a String of the form - * "old-address new-address". This function returns the new address mapping. - * + /** Tells the Tor server that future SOCKS requests for connections to a + * set of original addresses should be replaced with connections to the + * specified replacement addresses. Each element of <b>kvLines</b> is a + * String of the form "old-address new-address". This function returns the + * new address mapping. + * <p> * The client may decline to provide a body for the original address, and - * instead send a special null address ("0.0.0.0" for IPv4, "::0" for IPv6, or - * "." for hostname), signifying that the server should choose the original - * address itself, and return that address in the reply. The server - * should ensure that it returns an element of address space that is unlikely - * to be in actual use. If there is already an address mapped to the - * destination address, the server may reuse that mapping. - * - * If the original address is already mapped to a different address, the old - * mapping is removed. If the original address and the destination address - * are the same, the server removes any mapping in place for the original - * address. - * - * Mappings set by the controller last until the Tor process exits: - * they never expire. If the controller wants the mapping to last only - * a certain time, then it must explicitly un-map the address when that - * time has elapsed. + * instead send a special null address ("0.0.0.0" for IPv4, "::0" for IPv6, + * or "." for hostname), signifying that the server should choose the + * original address itself, and return that address in the reply. The + * server should ensure that it returns an element of address space that is + * unlikely to be in actual use. If there is already an address mapped to + * the destination address, the server may reuse that mapping. + * <p> + * If the original address is already mapped to a different address, the + * old mapping is removed. If the original address and the destination + * address are the same, the server removes any mapping in place for the + * original address. + * <p> + * Mappings set by the controller last until the Tor process exits: they + * never expire. If the controller wants the mapping to last only a certain + * time, then it must explicitly un-map the address when that time has + * elapsed. */ - public Map<String,String> mapAddresses(Collection<String> kvLines) throws IOException { + public Map<String, String> mapAddresses(Collection<String> kvLines) + throws IOException { StringBuffer sb = new StringBuffer("MAPADDRESS"); - for (Iterator<String> it = kvLines.iterator(); it.hasNext(); ) { - String kv = it.next(); - int i = kv.indexOf(' '); - sb.append(" ").append(kv.substring(0,i)).append("=") - .append(quote(kv.substring(i+1))); + for(String kv : kvLines) { + int idx = kv.indexOf(' '); + sb.append(" ").append(kv.substring(0, idx)); + sb.append("=").append(quote(kv.substring(idx + 1))); } sb.append("\r\n"); List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null); - Map<String,String> result = new HashMap<String,String>(); - for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) { - String kv = (it.next()).msg; + Map<String, String> result = new HashMap<String, String>(); + for(ReplyLine line : lst) { + String kv = line.msg; int idx = kv.indexOf('='); - result.put(kv.substring(0, idx), - kv.substring(idx+1)); + result.put(kv.substring(0, idx), kv.substring(idx + 1)); } return result; } - public Map<String,String> mapAddresses(Map<String,String> addresses) throws IOException { + public Map<String, String> mapAddresses(Map<String, String> addresses) + throws IOException { List<String> kvList = new ArrayList<String>(); - for (Iterator<Map.Entry<String, String>> it = addresses.entrySet().iterator(); it.hasNext(); ) { - Map.Entry<String,String> e = it.next(); - kvList.add(e.getKey()+" "+e.getValue()); + for(Map.Entry<String, String> e : addresses.entrySet()) { + kvList.add(e.getKey() + " " + e.getValue()); } return mapAddresses(kvList); } - public String mapAddress(String fromAddr, String toAddr) throws IOException { + public String mapAddress(String fromAddr, String toAddr) + throws IOException { List<String> lst = new ArrayList<String>(); - lst.add(fromAddr+" "+toAddr+"\n"); - Map<String,String> m = mapAddresses(lst); + lst.add(fromAddr + " " + toAddr + "\n"); + Map<String, String> m = mapAddresses(lst); return m.get(fromAddr); } - /** Queries the Tor server for keyed values that are not stored in the torrc - * configuration file. Returns a map of keys to values. - * + /** Queries the Tor server for keyed values that are not stored in the + * torrc configuration file. Returns a map of keys to values. + * <p> * Recognized keys include: * <ul> * <li>"version" : The version of the server's software, including the name * of the software. (example: "Tor 0.0.9.4")</li> - * <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest server - * descriptor for a given OR, NUL-terminated. If no such OR is known, the - * corresponding value is an empty string.</li> - * <li>"network-status" : a space-separated list of all known OR identities. - * This is in the same format as the router-status line in directories; - * see tor-spec.txt for details.</li> + * <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest + * server descriptor for a given OR, NUL-terminated. If no such OR is + * known, the corresponding value is an empty string.</li> + * <li>"network-status" : a space-separated list of all known OR + * identities. This is in the same format as the router-status line in + * directories; see tor-spec.txt for details.</li> * <li>"addr-mappings/all"</li> * <li>"addr-mappings/config"</li> * <li>"addr-mappings/cache"</li> - * <li>"addr-mappings/control" : a space-separated list of address mappings, each - * in the form of "from-address=to-address". The 'config' key - * returns those address mappings set in the configuration; the 'cache' + * <li>"addr-mappings/control" : a space-separated list of address + * mappings, each in the form of "from-address=to-address". The 'config' + * key returns those address mappings set in the configuration; the 'cache' * key returns the mappings in the client-side DNS cache; the 'control' * key returns the mappings set via the control interface; the 'all' * target returns the mappings set through any mechanism.</li> - * <li>"circuit-status" : A series of lines as for a circuit status event. Each line is of the form: - * "CircuitID CircStatus Path"</li> - * <li>"stream-status" : A series of lines as for a stream status event. Each is of the form: - * "StreamID StreamStatus CircID Target"</li> - * <li>"orconn-status" : A series of lines as for an OR connection status event. Each is of the - * form: "ServerID ORStatus"</li> + * <li>"circuit-status" : A series of lines as for a circuit status event. + * Each line is of the form: "CircuitID CircStatus Path"</li> + * <li>"stream-status" : A series of lines as for a stream status event. + * Each is of the form: "StreamID StreamStatus CircID Target"</li> + * <li>"orconn-status" : A series of lines as for an OR connection status + * event. Each is of the form: "ServerID ORStatus"</li> * </ul> */ - public Map<String,String> getInfo(Collection<String> keys) throws IOException { + public Map<String, String> getInfo(Collection<String> keys) + throws IOException { StringBuffer sb = new StringBuffer("GETINFO"); - for (Iterator<String> it = keys.iterator(); it.hasNext(); ) { - sb.append(" ").append(it.next()); + for(String key : keys) { + sb.append(" ").append(key); } sb.append("\r\n"); List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null); - Map<String,String> m = new HashMap<String,String>(); - for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) { - ReplyLine line = it.next(); + Map<String, String> m = new HashMap<String, String>(); + for(ReplyLine line : lst) { int idx = line.msg.indexOf('='); - if (idx<0) - break; - String k = line.msg.substring(0,idx); + if(idx < 0) break; + String k = line.msg.substring(0, idx); String v; - if (line.rest != null) { - v = line.rest; - } else { - v = line.msg.substring(idx+1); - } + if(line.rest != null) v = line.rest; + else v = line.msg.substring(idx + 1); m.put(k, v); } return m; } - - - /** Return the value of the information field 'key' */ + /** Returns the value of the information field 'key' */ public String getInfo(String key) throws IOException { - List<String> lst = new ArrayList<String>(); - lst.add(key); - Map<String,String> m = getInfo(lst); + Map<String, String> m = getInfo(Arrays.asList(key)); return m.get(key); } - /** An extendCircuit request takes one of two forms: either the <b>circID</b> is zero, in - * which case it is a request for the server to build a new circuit according - * to the specified path, or the <b>circID</b> is nonzero, in which case it is a - * request for the server to extend an existing circuit with that ID according - * to the specified <b>path</b>. - * - * If successful, returns the Circuit ID of the (maybe newly created) circuit. + /** An extendCircuit request takes one of two forms: either the + * <b>circID</b> is zero, in which case it is a request for the server to + * build a new circuit according to the specified path, or the + * <b>circID</b> is nonzero, in which case it is a request for the server + * to extend an existing circuit with that ID according to the specified + * <b>path</b>. + * <p> + * If successful, returns the Circuit ID of the (maybe newly created) + * circuit. */ public String extendCircuit(String circID, String path) throws IOException { - List<ReplyLine> lst = sendAndWaitForResponse( - "EXTENDCIRCUIT "+circID+" "+path+"\r\n", null); - return (lst.get(0)).msg; + String cmd = "EXTENDCIRCUIT " + circID + " " + path + "\r\n"; + List<ReplyLine> lst = sendAndWaitForResponse(cmd, null); + return lst.get(0).msg; } - /** Informs the Tor server that the stream specified by <b>streamID</b> should be - * associated with the circuit specified by <b>circID</b>. - * - * Each stream may be associated with - * at most one circuit, and multiple streams may share the same circuit. - * Streams can only be attached to completed circuits (that is, circuits that - * have sent a circuit status "BUILT" event or are listed as built in a - * getInfo circuit-status request). - * + /** Informs the Tor server that the stream specified by <b>streamID</b> + * should be associated with the circuit specified by <b>circID</b>. + * <p> + * Each stream may be associated with at most one circuit, and multiple + * streams may share the same circuit. Streams can only be attached to + * completed circuits (that is, circuits that have sent a circuit status + * "BUILT" event or are listed as built in a getInfo circuit-status + * request). + * <p> * If <b>circID</b> is 0, responsibility for attaching the given stream is * returned to Tor. - * - * By default, Tor automatically attaches streams to - * circuits itself, unless the configuration variable - * "__LeaveStreamsUnattached" is set to "1". Attempting to attach streams - * via TC when "__LeaveStreamsUnattached" is false may cause a race between - * Tor and the controller, as both attempt to attach streams to circuits. + * <p> + * By default, Tor automatically attaches streams to circuits itself, + * unless the configuration variable "__LeaveStreamsUnattached" is set to + * "1". Attempting to attach streams via TC when + * "__LeaveStreamsUnattached" is false may cause a race between Tor and the + * controller, as both attempt to attach streams to circuits. */ public void attachStream(String streamID, String circID) throws IOException { - sendAndWaitForResponse("ATTACHSTREAM "+streamID+" "+circID+"\r\n", null); + String cmd = "ATTACHSTREAM " + streamID + " " + circID + "\r\n"; + sendAndWaitForResponse(cmd, null); } /** Tells Tor about the server descriptor in <b>desc</b>. - * + * <p> * The descriptor, when parsed, must contain a number of well-specified * fields, including fields for its nickname and identity. */ // More documentation here on format of desc? // No need for return value? control-spec.txt says reply is merely "250 OK" on success... public String postDescriptor(String desc) throws IOException { - List<ReplyLine> lst = sendAndWaitForResponse("+POSTDESCRIPTOR\r\n", desc); - return (lst.get(0)).msg; + String cmd = "+POSTDESCRIPTOR\r\n"; + List<ReplyLine> lst = sendAndWaitForResponse(cmd, desc); + return lst.get(0).msg; } - /** Tells Tor to change the exit address of the stream identified by <b>streamID</b> - * to <b>address</b>. No remapping is performed on the new provided address. - * - * To be sure that the modified address will be used, this event must be sent - * after a new stream event is received, and before attaching this stream to - * a circuit. - */ - public void redirectStream(String streamID, String address) throws IOException { - sendAndWaitForResponse("REDIRECTSTREAM "+streamID+" "+address+"\r\n", - null); + /** Tells Tor to change the exit address of the stream identified by + * <b>streamID</b> to <b>address</b>. No remapping is performed on the new + * provided address. + * <p> + * To be sure that the modified address will be used, this event must be + * sent after a new stream event is received, and before attaching this + * stream to a circuit. + */ + public void redirectStream(String streamID, String address) + throws IOException { + String cmd = "REDIRECTSTREAM " + streamID + " " + address + "\r\n"; + sendAndWaitForResponse(cmd, null); } /** Tells Tor to close the stream identified by <b>streamID</b>. - * <b>reason</b> should be one of the Tor RELAY_END reasons given in tor-spec.txt, as a decimal: + * <b>reason</b> should be one of the Tor RELAY_END reasons given in + * tor-spec.txt, as a decimal: * <ul> * <li>1 -- REASON_MISC (catch-all for unlisted reasons)</li> * <li>2 -- REASON_RESOLVEFAILED (couldn't look up hostname)</li> * <li>3 -- REASON_CONNECTREFUSED (remote host refused connection)</li> - * <li>4 -- REASON_EXITPOLICY (OR refuses to connect to host or port)</li> + * <li>4 -- REASON_EXITPOLICY (OR refuses to connect to host or + * port)</li> * <li>5 -- REASON_DESTROY (Circuit is being destroyed)</li> - * <li>6 -- REASON_DONE (Anonymized TCP connection was closed)</li> - * <li>7 -- REASON_TIMEOUT (Connection timed out, or OR timed out while connecting)</li> + * <li>6 -- REASON_DONE (Anonymized TCP connection was + * closed)</li> + * <li>7 -- REASON_TIMEOUT (Connection timed out, or OR timed out + * while connecting)</li> * <li>8 -- (unallocated)</li> * <li>9 -- REASON_HIBERNATING (OR is temporarily hibernating)</li> * <li>10 -- REASON_INTERNAL (Internal error at the OR)</li> - * <li>11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill request)</li> + * <li>11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill + * request)</li> * <li>12 -- REASON_CONNRESET (Connection was unexpectedly reset)</li> - * <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of Tor protocol violations)</li> + * <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of + * Tor protocol violations)</li> * </ul> - * - * Tor may hold the stream open for a while to flush any data that is pending. + * Tor may hold the stream open for a while to flush any data that is + * pending. */ - public void closeStream(String streamID, byte reason) - throws IOException { - sendAndWaitForResponse("CLOSESTREAM "+streamID+" "+reason+"\r\n",null); + public void closeStream(String streamID, byte reason) throws IOException { + String cmd = "CLOSESTREAM " + streamID + " " + reason + "\r\n"; + sendAndWaitForResponse(cmd, null); } /** Tells Tor to close the circuit identified by <b>circID</b>. - * If <b>ifUnused</b> is true, do not close the circuit unless it is unused. + * If <b>ifUnused</b> is true, do not close the circuit unless it is + * unused. */ - public void closeCircuit(String circID, boolean ifUnused) throws IOException { - sendAndWaitForResponse("CLOSECIRCUIT "+circID+ - (ifUnused?" IFUNUSED":"")+"\r\n", null); + public void closeCircuit(String circID, boolean ifUnused) + throws IOException { + String cmd; + if(ifUnused) cmd = "CLOSECIRCUIT " + circID + " IFUNUSED\r\n"; + else cmd = "CLOSECIRCUIT " + circID + "\r\n"; + sendAndWaitForResponse(cmd, null); } } diff -Bbur jtorctl/net/freehaven/tor/control/TorControlError.java jtorctl-briar/net/freehaven/tor/control/TorControlError.java --- jtorctl/net/freehaven/tor/control/TorControlError.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlError.java 2013-05-16 19:56:30.000000000 +0100 @@ -2,13 +2,15 @@ // See LICENSE file for copying information package net.freehaven.tor.control; -/** - * An exception raised when Tor tells us about an error. - */ -public class TorControlError extends RuntimeException { - static final long serialVersionUID = 2; +import java.io.IOException; + +/** An exception raised when Tor tells us about an error. */ +public class TorControlError extends IOException { + + private static final long serialVersionUID = 2; + + private final int errorType; - int errorType; public TorControlError(int type, String s) { super(s); errorType = type; @@ -19,13 +23,13 @@ public int getErrorType() { return errorType; } + public String getErrorMsg() { try { - if (errorType == -1) - return null; + if(errorType == -1) return null; return TorControlCommands.ERROR_MSGS[errorType]; - } catch (ArrayIndexOutOfBoundsException ex) { - return "Unrecongized error #"+errorType; + } catch(ArrayIndexOutOfBoundsException ex) { + return "Unrecongized error #" + errorType; } } } diff -Bbur jtorctl/net/freehaven/tor/control/TorControlSyntaxError.java jtorctl-briar/net/freehaven/tor/control/TorControlSyntaxError.java --- jtorctl/net/freehaven/tor/control/TorControlSyntaxError.java 2013-04-24 16:46:08.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlSyntaxError.java 2013-05-16 19:56:30.000000000 +0100 @@ -2,12 +2,15 @@ // See LICENSE file for copying information package net.freehaven.tor.control; -/** - * An exception raised when Tor behaves in an unexpected way. - */ -public class TorControlSyntaxError extends RuntimeException { - static final long serialVersionUID = 2; +import java.io.IOException; - public TorControlSyntaxError(String s) { super(s); } +/** An exception raised when Tor behaves in an unexpected way. */ +public class TorControlSyntaxError extends IOException { + + private static final long serialVersionUID = 2; + + public TorControlSyntaxError(String s) { + super(s); + } }