diff --git a/briar-android/libs/jtorctl-briar.jar b/briar-android/libs/jtorctl-briar.jar index 1f90213fca321569b1bb89cb8b566c2dcc24be2f..f5753c6337180ddae63602370594f05745b3af22 100644 Binary files a/briar-android/libs/jtorctl-briar.jar and b/briar-android/libs/jtorctl-briar.jar differ diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index f8c225e9eed02f03f2eb02af0cb96376a2c97e05..cdf700b85a235983573eb8e0d8e9d41314c4c8f6 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -21,7 +21,6 @@ import java.net.Socket; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Scanner; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -546,34 +545,14 @@ class TorPlugin implements DuplexPlugin, EventHandler { throw new UnsupportedOperationException(); } - public void circuitStatus(String status, String id, List<String> path, - Map<String, String> info) { - if(LOG.isLoggable(INFO)) { - String msg = "Circuit " + id + " " + status; - String purpose = info.get("PURPOSE"); - if(purpose != null) msg += ", purpose: " + purpose; - String hsState = info.get("HS_STATE"); - if(hsState != null) msg += ", state: " + hsState; - String rendQuery = info.get("REND_QUERY"); - if(rendQuery != null) msg += ", service: " + rendQuery; - if(!path.isEmpty()) msg += ", path: " + shortenPath(path); - LOG.info(msg); - } + public void circuitStatus(String status, String id, String path) { + if(LOG.isLoggable(INFO)) LOG.info("Circuit " + id + " " + status); if(status.equals("BUILT") && !circuitBuilt.getAndSet(true)) { LOG.info("First circuit built"); if(isRunning()) callback.pollNow(); } } - private String shortenPath(List<String> path) { - StringBuilder s = new StringBuilder(); - for(String id : path) { - if(s.length() > 0) s.append(','); - s.append(id.substring(1, 7)); - } - return s.toString(); - } - public void streamStatus(String status, String id, String target) {} public void orConnStatus(String status, String orName) { diff --git a/jtorctl.patch b/jtorctl.patch index 2375100a8046947f981112f921cb1e0fb6fcfa4e..80fee4767a7498bad56cd2d11b95c85189f09774 100644 --- a/jtorctl.patch +++ b/jtorctl.patch @@ -1,1064 +1,16 @@ -diff -Bbur jtorctl/net/freehaven/tor/control/EventHandler.java jtorctl-briar/net/freehaven/tor/control/EventHandler.java ---- jtorctl/net/freehaven/tor/control/EventHandler.java 2014-04-02 11:26:56.000000000 +0100 -+++ jtorctl-briar/net/freehaven/tor/control/EventHandler.java 2014-05-14 16:54:29.291301601 +0100 -@@ -2,6 +2,9 @@ - // See LICENSE file for copying information - package net.freehaven.tor.control; - -+import java.util.List; -+import java.util.Map; -+ - /** - * Abstract interface whose methods are invoked when Tor sends us an event. - * -@@ -20,10 +23,21 @@ - * <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. -+ * <b>circID</b> is the alphanumeric identifier of the affected circuit. -+ * <b>path</b> contains the alphanumeric ServerIDs of the circuit's routers. -+ * <b>info</b> may include some or all of the following entries: -+ * <ul> -+ * <li>BUILD_FLAGS: a comma-separated list of the circuit's build -+ * flags.</li> -+ * <li>PURPOSE: the purpose of the circuit.</li> -+ * <li>HS_STATE: the state of the circuit if it is a hidden service -+ * circuit.</li> -+ * <li>REND_QUERY: the hidden service address if the circuit is a -+ * hidden service circuit.</li> -+ * </ul> - */ -- public void circuitStatus(String status, String circID, String path); -+ public void circuitStatus(String status, String circID, List<String> path, -+ Map<String, String> info); - /** - * Invoked when a stream's status has changed. - * Possible values for <b>status</b> are: -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/NullEventHandler.java jtorctl-briar/net/freehaven/tor/control/NullEventHandler.java ---- jtorctl/net/freehaven/tor/control/NullEventHandler.java 2014-04-02 11:26:56.000000000 +0100 -+++ jtorctl-briar/net/freehaven/tor/control/NullEventHandler.java 2014-05-14 16:54:43.219370671 +0100 -@@ -2,12 +2,16 @@ - // See LICENSE file for copying information - package net.freehaven.tor.control; - -+import java.util.List; -+import java.util.Map; -+ - /** - * Implementation of EventHandler that ignores all events. Useful - * when you only want to override one method. - */ - public class NullEventHandler implements EventHandler { -- public void circuitStatus(String status, String circID, String path) {} -+ public void circuitStatus(String status, String circID, List<String> path, -+ Map<String, String> info) {} - public void streamStatus(String status, String streamID, String target) {} - public void orConnStatus(String status, String orName) {} - public void bandwidthUsed(long read, long written) {} -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-05-14 16:59:18.428735366 +0100 -@@ -2,96 +2,94 @@ - // 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.Collections; - 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 +108,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 +125,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 +150,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 +169,9 @@ - return reply; +--- jtorctl/net/freehaven/tor/control/TorControlConnection.java 2014-10-03 12:21:51.883098440 +0100 ++++ jtorctl-briar/net/freehaven/tor/control/TorControlConnection.java 2014-10-03 12:17:07.429687913 +0100 +@@ -728,5 +728,12 @@ + sendAndWaitForResponse("CLOSECIRCUIT "+circID+ + (ifUnused?" IFUNUSED":"")+"\r\n", null); } - -- 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 +183,58 @@ - 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(' '); -+ if (idx == -1) -+ continue; - 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)); -+ int size = lst.size(), firstKeyValue; -+ List<String> path; -+ if (size < 3 || lst.get(1).equals("LAUNCHED")) { -+ path = Collections.emptyList(); -+ firstKeyValue = 2; -+ } else { -+ path = Arrays.asList(lst.get(2).split(",")); -+ path = Collections.unmodifiableList(path); -+ firstKeyValue = 3; -+ } -+ Map<String, String> info = new HashMap<String, String>(); -+ for (int i = firstKeyValue; i < size; i++) { -+ String kv = lst.get(i); -+ idx = kv.indexOf('='); -+ if (idx >= 0) { -+ String key = kv.substring(0, idx); -+ String value = kv.substring(idx+1); -+ info.put(key, value); -+ } -+ } -+ info = Collections.unmodifiableMap(info); -+ handler.circuitStatus(lst.get(1), lst.get(0), path, info); - } 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 +258,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 +288,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 +334,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 +351,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 +389,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 +406,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,61 +417,63 @@ - * 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; - } - - /** Request that the server inform the client about interesting events. - * Each element of <b>events</b> is one of the following Strings: -- * ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" | -- * "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] . -- * -+ * ["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 +511,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 +522,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 +530,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,17 +604,16 @@ - * </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) -+ if (idx == -1) - break; - String k = line.msg.substring(0,idx); - String v; -@@ -629,13 +627,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 +638,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 +678,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 +713,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 +721,15 @@ - * 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); -+ } + + /** Tells Tor to exit when this control connection is closed. This command + * was added in Tor 0.2.2.28-beta. + */ + public void takeOwnership() throws IOException { + sendAndWaitForResponse("TAKEOWNERSHIP\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); -+ } -+}