Skip to content
Snippets Groups Projects
jtorctl.patch 38.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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 {
     
    
                 } 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;
    
         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);
    
     
         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());
    
         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;
     
    
      */
     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() {
    
                 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()) {
    
    @@ -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) {
    
             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 msg = line.substring(4);
                 String rest = null;
    
                 if (c == '+') {
    -                StringBuffer data = new StringBuffer();
    +                StringBuilder data = new StringBuilder();
                     while (true) {
    
                         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));
    
                 } 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);
    
             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();
    
                     // connection has been closed remotely! end the loop!
                     return;
                 }
    -            if ((lst.get(0)).status.startsWith("6"))
    
    +            if ((lst.get(0)).status.startsWith("6")) {
    
                     synchronized (waiters) {
    
    @@ -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");
    
    @@ -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".
    -     *
    
          * 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.
    
          * 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.
    
          * 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);
    
         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.
    
          * If an option appears multiple times in the configuration, all of its
          * key-value pairs are returned in order.
    -     *
    
          * 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 {
    
    @@ -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"] .
    -     * 
    
          * 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.
    -     *
    
          * 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:".
    
          * You can generate the salt of a password by calling
    -     *       'tor --hash-password <password>'
    
    +     * <tt>'tor --hash-password &lt;password&gt;'</tt>
    
          * or by using the provided PasswordDigest class.
    
          * To authenticate under this scheme, the controller sends Tor the original
          * secret that was used to generate the password.
          */
    
             if (debugOutput != null)
                 debugOutput.print(">> "+s);
    
    -        if (this.thread != null) {
    -            this.thread.stopListening();
    -    	}
    
             synchronized (waiters) {
    
    @@ -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.
    
          * 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));
    
         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());
    
         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));
    
         /** Queries the Tor server for keyed values that are not stored in the torrc
          * configuration file.  Returns a map of keys to values.
    
          * Recognized keys include:
          * <ul>
          * <li>"version" : The version of the server's software, including the name
    
    @@ -605,15 +583,14 @@
    
         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));
    
    @@ -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;
    
         /** 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).
    
          * 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>.
    -     * 
    
          * 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;
    
         /** 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);
    +    }
    +}