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 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/examples/DebuggingEventHandler.java 2014-04-02 11:31:48.000000000 +0100 @@ -3,12 +3,12 @@ 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; @@ -27,11 +30,13 @@ 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()); } 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 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/examples/Main.java 2014-04-02 11:56:39.000000000 +0100 @@ -2,14 +2,17 @@ // 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.*; public class Main implements TorControlCommands { @@ -34,27 +37,22 @@ } 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 (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; @@ -87,9 +85,9 @@ 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(); + 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); } @@ -97,9 +95,9 @@ 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(); + 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()); } @@ -108,10 +106,7 @@ 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> lst = Arrays.asList(args).subList(1, args.length); conn.setEventHandler( new DebuggingEventHandler(new PrintWriter(System.out, true))); conn.setEvents(lst); @@ -130,17 +125,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 java.net.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 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/PasswordDigest.java 2014-04-02 12:29:20.000000000 +0100 @@ -2,6 +2,7 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.MessageDigest; @@ -13,8 +14,8 @@ */ 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() { @@ -41,7 +42,7 @@ 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. @@ -63,7 +64,7 @@ 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; @@ -86,12 +87,5 @@ 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/TorControlConnection.java jtorctl-briar/net/freehaven/tor/control/TorControlConnection.java --- jtorctl/net/freehaven/tor/control/TorControlConnection.java 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlConnection.java 2014-04-02 12:29:34.000000000 +0100 @@ -2,96 +2,93 @@ // 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 { + + synchronized List<ReplyLine> getResponse() throws InterruptedException { while (response == null) { wait(); } - } catch (InterruptedException ex) { - throw new CancellationException( - "Please don't interrupt library calls."); - } 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; + public TorControlConnection(Reader i, Writer o) { + if (i instanceof BufferedReader) + input = (BufferedReader) i; else - this.input = new java.io.BufferedReader(i); - - this.waiters = new LinkedList<Waiter>(); + input = new BufferedReader(i); + output = o; + waiters = new LinkedList<Waiter>(); } - protected final void writeEscaped(String s) throws IOException { + private final void writeEscaped(String s) throws IOException { StringTokenizer st = new StringTokenizer(s, "\n"); while (st.hasMoreTokens()) { String line = st.nextToken(); @@ -110,12 +107,11 @@ debugOutput.print(">> .\n"); } - protected static final String quote(String s) { - StringBuffer sb = new StringBuffer("\""); + private static final String quote(String s) { + StringBuilder sb = new StringBuilder("\""); for (int i = 0; i < s.length(); ++i) { char c = s.charAt(i); - switch (c) - { + switch (c) { case '\r': case '\n': case '\\': @@ -128,7 +124,7 @@ 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 { @@ -153,7 +149,7 @@ String msg = line.substring(4); String rest = null; if (c == '+') { - StringBuffer data = new StringBuffer(); + StringBuilder data = new StringBuilder(); while (true) { line = input.readLine(); if (debugOutput != null) @@ -172,8 +168,9 @@ 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) @@ -185,38 +182,38 @@ 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.toString()); + } + 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) { + private void handleEvent(ArrayList<ReplyLine> events) { if (handler == null) return; - for (Iterator<ReplyLine> i = events.iterator(); i.hasNext(); ) { - ReplyLine line = i.next(); + 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")) { 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)); + String path; + if (lst.get(1).equals("LAUNCHED") || lst.size() < 3) 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")) { List<String> lst = Bytes.splitStr(null, rest); @@ -240,23 +237,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,50 +267,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); 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); + parseThreadException = ex; } } - public void stopListening() { - this.stopped = true; - } } - protected final void checkThread() { + private synchronized void checkThread() { if (thread == null) launchThread(true); } /** helper: implement the main background loop. */ - protected void react() throws IOException { + private void react() throws IOException { while (true) { ArrayList<ReplyLine> lst = readReply(); 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) { w = waiters.removeFirst(); @@ -324,20 +313,16 @@ } } - /** Change the value of the configuration option 'key' to 'val'. - */ + /** 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 +330,33 @@ /** 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. - * + * <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. - * + * <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. */ public void setConf(Collection<String> kvList) throws IOException { if (kvList.size() == 0) return; - StringBuffer b = new StringBuffer("SETCONF"); - for (Iterator<String> it = kvList.iterator(); it.hasNext(); ) { - String kv = it.next(); + StringBuilder b = new StringBuilder("SETCONF"); + for (String kv : kvList) { int i = kv.indexOf(' '); if (i == -1) b.append(" ").append(kv); - b.append(" ").append(kv.substring(0,i)).append("=") - .append(quote(kv.substring(i+1))); + b.append(" ").append(kv.substring(0,i)).append("="); + b.append(quote(kv.substring(i+1))); } b.append("\r\n"); sendAndWaitForResponse(b.toString(), null); @@ -384,9 +368,8 @@ public void resetConf(Collection<String> keys) throws IOException { if (keys.size() == 0) return; - StringBuffer b = new StringBuffer("RESETCONF"); - for (Iterator<String> it = keys.iterator(); it.hasNext(); ) { - String key = it.next(); + StringBuilder b = new StringBuilder("RESETCONF"); + for (String key : keys) { b.append(" ").append(key); } b.append("\r\n"); @@ -402,10 +385,10 @@ /** 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" @@ -413,23 +396,24 @@ * HiddenServiceNodes, and HiddenServiceExcludeNodes option settings. */ 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(); + StringBuilder sb = new StringBuilder("GETCONF"); + 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 +421,38 @@ * 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. */ 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()); + StringBuilder sb = new StringBuilder("SETEVENTS"); + 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]). - * + * <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. */ @@ -505,9 +490,6 @@ Waiter w = new Waiter(); if (debugOutput != null) debugOutput.print(">> "+s); - if (this.thread != null) { - this.thread.stopListening(); - } synchronized (waiters) { output.write(s); output.flush(); @@ -519,7 +501,7 @@ * 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 @@ -527,56 +509,52 @@ * 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 { - StringBuffer sb = new StringBuffer("MAPADDRESS"); - for (Iterator<String> it = kvLines.iterator(); it.hasNext(); ) { - String kv = it.next(); + StringBuilder sb = new StringBuilder("MAPADDRESS"); + for (String kv : kvLines) { int i = kv.indexOf(' '); - sb.append(" ").append(kv.substring(0,i)).append("=") - .append(quote(kv.substring(i+1))); + sb.append(" ").append(kv.substring(0,i)).append("="); + sb.append(quote(kv.substring(i+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; + 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 { 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(); + 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 { - List<String> lst = new ArrayList<String>(); - lst.add(fromAddr+" "+toAddr+"\n"); - Map<String,String> m = mapAddresses(lst); + String s = fromAddr+" "+toAddr+"\n"; + Map<String,String> m = mapAddresses(Arrays.asList(s)); 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. - * + * <p> * Recognized keys include: * <ul> * <li>"version" : The version of the server's software, including the name @@ -605,15 +583,14 @@ * </ul> */ 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()); + StringBuilder sb = new StringBuilder("GETINFO"); + 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(); + for (ReplyLine line : lst) { int idx = line.msg.indexOf('='); if (idx<0) break; @@ -629,13 +606,9 @@ 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); } @@ -644,40 +617,39 @@ * 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; + 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>. - * + * <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. - * + * <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 { + public void attachStream(String streamID, String circID) throws IOException { sendAndWaitForResponse("ATTACHSTREAM "+streamID+" "+circID+"\r\n", 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. */ @@ -685,12 +657,12 @@ // 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; + 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. - * + * <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. @@ -720,8 +692,7 @@ * * 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 { + public void closeStream(String streamID, byte reason) throws IOException { sendAndWaitForResponse("CLOSESTREAM "+streamID+" "+reason+"\r\n",null); } @@ -729,8 +700,8 @@ * 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); + String arg = ifUnused ? " IFUNUSED" : ""; + sendAndWaitForResponse("CLOSECIRCUIT "+circID+arg+"\r\n", 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 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlError.java 2014-04-02 12:28:01.000000000 +0100 @@ -2,13 +2,17 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.io.IOException; + /** * An exception raised when Tor tells us about an error. */ -public class TorControlError extends RuntimeException { - static final long serialVersionUID = 2; +public class TorControlError extends IOException { + + private static final long serialVersionUID = 3; + + private final int errorType; - int errorType; public TorControlError(int type, String s) { super(s); errorType = type; diff -Bbur jtorctl/net/freehaven/tor/control/TorControlSyntaxError.java jtorctl-briar/net/freehaven/tor/control/TorControlSyntaxError.java --- jtorctl/net/freehaven/tor/control/TorControlSyntaxError.java 2014-04-02 11:26:56.000000000 +0100 +++ jtorctl-briar/net/freehaven/tor/control/TorControlSyntaxError.java 2014-04-02 12:27:52.000000000 +0100 @@ -2,12 +2,16 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.io.IOException; + /** * An exception raised when Tor behaves in an unexpected way. */ -public class TorControlSyntaxError extends RuntimeException { - static final long serialVersionUID = 2; +public class TorControlSyntaxError extends IOException { - public TorControlSyntaxError(String s) { super(s); } -} + private static final long serialVersionUID = 3; + public TorControlSyntaxError(String s) { + super(s); + } +}