diff --git a/net/freehaven/tor/control/PasswordDigest.java b/net/freehaven/tor/control/PasswordDigest.java index 680db7b..3b7e9e8 100644 --- a/net/freehaven/tor/control/PasswordDigest.java +++ b/net/freehaven/tor/control/PasswordDigest.java @@ -2,8 +2,9 @@ // See LICENSE file for copying information package net.freehaven.tor.control; -import java.security.SecureRandom; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; /** * A hashed digest of a secret password (used to set control connection @@ -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() { @@ -63,7 +64,7 @@ public static byte[] secretToKey(byte[] secret, byte[] specifier) { 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; diff --git a/net/freehaven/tor/control/TorControlConnection.java b/net/freehaven/tor/control/TorControlConnection.java index 0fd9233..6c80f17 100644 --- a/net/freehaven/tor/control/TorControlConnection.java +++ b/net/freehaven/tor/control/TorControlConnection.java @@ -2,7 +2,17 @@ // See LICENSE file for copying information package net.freehaven.tor.control; +import java.io.BufferedReader; import java.io.IOException; +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.net.SocketException; import java.util.ArrayList; import java.util.Collection; @@ -15,79 +25,66 @@ 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 waiters; + private final BufferedReader input; + private final Writer output; - protected LinkedList waiters; + private ControlParseThread thread; // Locking: this - protected ControlParseThread thread; - - protected java.io.BufferedReader input; + private volatile EventHandler handler; + private volatile PrintWriter debugOutput; + private volatile IOException parseThreadException; - protected java.io.Writer output; + static class Waiter { - protected java.io.PrintWriter debugOutput; + List response; // Locking: this - static class Waiter { - List response; - public synchronized List getResponse() { - try { + synchronized List 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 response) { + + synchronized void setResponse(List response) { this.response = response; notifyAll(); } } static class ReplyLine { - public String status; - public String msg; - public String rest; + + 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; } } - 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 { + public TorControlConnection(Socket connection) throws IOException { this(connection.getInputStream(), connection.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) { + public TorControlConnection(Reader i, Writer o) { this.output = o; - if (i instanceof java.io.BufferedReader) - this.input = (java.io.BufferedReader) i; + if (i instanceof BufferedReader) + this.input = (BufferedReader) i; else - this.input = new java.io.BufferedReader(i); - + this.input = new BufferedReader(i); this.waiters = new LinkedList(); } @@ -172,8 +169,9 @@ else if (line.startsWith(".")) return reply; } - protected synchronized List sendAndWaitForResponse(String s,String rest) - throws IOException { + protected synchronized List sendAndWaitForResponse(String s, + String rest) throws IOException { + if(parseThreadException != null) throw parseThreadException; checkThread(); Waiter w = new Waiter(); if (debugOutput != null) @@ -185,7 +183,12 @@ protected synchronized List sendAndWaitForResponse(String s,String re output.flush(); waiters.addLast(w); } - List lst = w.getResponse(); + List lst; + try { + lst = w.getResponse(); + } catch (InterruptedException ex) { + throw new IOException("Interrupted"); + } for (Iterator i = lst.iterator(); i.hasNext(); ) { ReplyLine c = i.next(); if (! c.status.startsWith("2")) @@ -210,7 +213,7 @@ protected void handleEvent(ArrayList events) { handler.circuitStatus(lst.get(1), lst.get(0), lst.get(1).equals("LAUNCHED") - || lst.size() < 2 ? "" + || lst.size() < 3 ? "" : lst.get(2)); } else if (tp.equals("STREAM")) { List lst = Bytes.splitStr(null, rest); @@ -246,7 +249,7 @@ protected void handleEvent(ArrayList events) { * 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; } @@ -255,8 +258,8 @@ public void setDebugging(java.io.PrintWriter w) { * 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,7 +274,7 @@ public void setEventHandler(EventHandler handler) { * 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); @@ -281,25 +284,18 @@ public Thread launchThread(boolean daemon) { } protected class ControlParseThread extends Thread { - boolean stopped = false; + @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() { + protected synchronized void checkThread() { if (thread == null) launchThread(true); } @@ -509,13 +505,9 @@ public void shutdownTor(String signal) throws IOException { Waiter w = new Waiter(); if (debugOutput != null) debugOutput.print(">> "+s); - if (this.thread != null) { - this.thread.stopListening(); - } synchronized (waiters) { output.write(s); output.flush(); - waiters.addLast(w); // Prevent react() from finding the list empty } } diff --git a/net/freehaven/tor/control/TorControlError.java b/net/freehaven/tor/control/TorControlError.java index fc6f11f..197c4a8 100644 --- a/net/freehaven/tor/control/TorControlError.java +++ b/net/freehaven/tor/control/TorControlError.java @@ -2,23 +2,30 @@ // 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 { + + static final long serialVersionUID = 3; + + private final int errorType; - int errorType; public TorControlError(int type, String s) { super(s); errorType = type; } + public TorControlError(String s) { this(-1, s); } + public int getErrorType() { return errorType; } + public String getErrorMsg() { try { if (errorType == -1) diff --git a/net/freehaven/tor/control/TorControlSyntaxError.java b/net/freehaven/tor/control/TorControlSyntaxError.java index 3c17561..af81d18 100644 --- a/net/freehaven/tor/control/TorControlSyntaxError.java +++ b/net/freehaven/tor/control/TorControlSyntaxError.java @@ -2,11 +2,14 @@ // 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 { + + static final long serialVersionUID = 3; public TorControlSyntaxError(String s) { super(s); } } diff --git a/net/freehaven/tor/control/examples/DebuggingEventHandler.java b/net/freehaven/tor/control/examples/DebuggingEventHandler.java index 2bd68cc..c2f5756 100644 --- a/net/freehaven/tor/control/examples/DebuggingEventHandler.java +++ b/net/freehaven/tor/control/examples/DebuggingEventHandler.java @@ -8,7 +8,7 @@ public class DebuggingEventHandler implements EventHandler { - protected PrintWriter out; + private final PrintWriter out; public DebuggingEventHandler(PrintWriter p) { out = p; diff --git a/net/freehaven/tor/control/examples/Main.java b/net/freehaven/tor/control/examples/Main.java index b8b52a9..3eb8129 100644 --- a/net/freehaven/tor/control/examples/Main.java +++ b/net/freehaven/tor/control/examples/Main.java @@ -3,8 +3,10 @@ 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; @@ -34,27 +36,22 @@ public static void main(String args[]) { } else { System.err.println("Unrecognized command: "+args[0]); } - } catch (java.io.EOFException ex) { + } catch (EOFException ex) { System.out.println("Control socket closed by Tor."); + } catch (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; @@ -132,15 +129,15 @@ public static void signal(String[] args) throws IOException { public static void authDemo(String[] args) throws IOException { PasswordDigest pwd = PasswordDigest.generateDigest(); - java.net.Socket s = new java.net.Socket("127.0.0.1", 9100); - TorControlConnection conn = TorControlConnection.getConnection(s); + Socket s = new Socket("127.0.0.1", 9100); + TorControlConnection conn = new TorControlConnection(s); conn.launchThread(true); conn.authenticate(new byte[0]); conn.setConf("HashedControlPassword", pwd.getHashedPassword()); - conn = TorControlConnection.getConnection( - new java.net.Socket("127.0.0.1", 9100)); + s = new Socket("127.0.0.1", 9100); + conn = new TorControlConnection(s); conn.launchThread(true); conn.authenticate(pwd.getSecret()); }