mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Add an absurd amount of logging.
This commit is contained in:
@@ -13,7 +13,6 @@ 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;
|
||||
import java.util.HashMap;
|
||||
@@ -22,11 +21,18 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** A connection to a running Tor process as specified in control-spec.txt. */
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
/**
|
||||
* A connection to a running Tor process as specified in control-spec.txt.
|
||||
*/
|
||||
public class TorControlConnection implements TorControlCommands {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(TorControlConnection.class.getName());
|
||||
|
||||
private final LinkedList<Waiter> waiters;
|
||||
private final BufferedReader input;
|
||||
private final Writer output;
|
||||
@@ -42,24 +48,45 @@ public class TorControlConnection implements TorControlCommands {
|
||||
List<ReplyLine> response; // Locking: this
|
||||
boolean interrupted;
|
||||
|
||||
synchronized List<ReplyLine> getResponse() throws InterruptedException {
|
||||
List<ReplyLine> getResponse() throws InterruptedException {
|
||||
LOG.info("Entering synchronized (waiter " + hashCode() + ")");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (waiter " + hashCode() + ")");
|
||||
while (response == null) {
|
||||
LOG.info("Waiter " + hashCode() + " waiting for response");
|
||||
wait();
|
||||
if (interrupted) {
|
||||
LOG.info("Waiter " + hashCode() + " interrupted");
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
LOG.info("Waiter " + hashCode() + " got response " + response);
|
||||
LOG.info("Leaving synchronized (waiter " + hashCode() + ")");
|
||||
return response;
|
||||
}
|
||||
|
||||
synchronized void setResponse(List<ReplyLine> response) {
|
||||
this.response = response;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void interrupt() {
|
||||
void setResponse(List<ReplyLine> response) {
|
||||
LOG.info("Entering synchronized (waiter " + hashCode() + ")");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (waiter " + hashCode() + ")");
|
||||
LOG.info("Setting response for waiter " + hashCode() + ": "
|
||||
+ response);
|
||||
this.response = response;
|
||||
notifyAll();
|
||||
LOG.info("Leaving synchronized (waiter " + hashCode() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
void interrupt() {
|
||||
LOG.info("Entering synchronized (waiter " + hashCode() + ")");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (waiter " + hashCode() + ")");
|
||||
LOG.info("Interrupting waiter " + hashCode());
|
||||
interrupted = true;
|
||||
notifyAll();
|
||||
LOG.info("Leaving synchronized (waiter " + hashCode() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,18 +97,28 @@ public class TorControlConnection implements TorControlCommands {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return status + " " + msg + " " + rest;
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a new TorControlConnection to communicate with Tor over
|
||||
/**
|
||||
* Create a new TorControlConnection to communicate with Tor over
|
||||
* a given socket. After calling this constructor, it is typical to
|
||||
* call launchThread and authenticate. */
|
||||
* call launchThread and authenticate.
|
||||
*/
|
||||
public TorControlConnection(Socket connection) throws IOException {
|
||||
this(connection.getInputStream(), connection.getOutputStream());
|
||||
}
|
||||
|
||||
/** Create a new TorControlConnection to communicate with Tor over
|
||||
/**
|
||||
* Create a new TorControlConnection to communicate with Tor over
|
||||
* an arbitrary pair of data streams.
|
||||
*/
|
||||
public TorControlConnection(InputStream i, OutputStream o) {
|
||||
@@ -94,7 +131,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
this.input = (BufferedReader) i;
|
||||
else
|
||||
this.input = new BufferedReader(i);
|
||||
this.waiters = new LinkedList<Waiter>();
|
||||
this.waiters = new LinkedList<>();
|
||||
}
|
||||
|
||||
protected final void writeEscaped(String s) throws IOException {
|
||||
@@ -102,13 +139,13 @@ public class TorControlConnection implements TorControlCommands {
|
||||
while (st.hasMoreTokens()) {
|
||||
String line = st.nextToken();
|
||||
if (line.startsWith("."))
|
||||
line = "."+line;
|
||||
line = "." + line;
|
||||
if (line.endsWith("\r"))
|
||||
line += "\n";
|
||||
else
|
||||
line += "\r\n";
|
||||
if (debugOutput != null)
|
||||
debugOutput.print(">> "+line);
|
||||
debugOutput.print(">> " + line);
|
||||
output.write(line);
|
||||
}
|
||||
output.write(".\r\n");
|
||||
@@ -116,12 +153,11 @@ public class TorControlConnection implements TorControlCommands {
|
||||
debugOutput.print(">> .\n");
|
||||
}
|
||||
|
||||
protected static final String quote(String s) {
|
||||
protected static String quote(String s) {
|
||||
StringBuffer sb = new StringBuffer("\"");
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
char c = s.charAt(i);
|
||||
switch (c)
|
||||
{
|
||||
switch (c) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\\':
|
||||
@@ -135,7 +171,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
}
|
||||
|
||||
protected final ArrayList<ReplyLine> readReply() throws IOException {
|
||||
ArrayList<ReplyLine> reply = new ArrayList<ReplyLine>();
|
||||
ArrayList<ReplyLine> reply = new ArrayList<>();
|
||||
char c;
|
||||
do {
|
||||
String line = input.readLine();
|
||||
@@ -151,10 +187,11 @@ public class TorControlConnection implements TorControlCommands {
|
||||
" broke down while receiving reply!");
|
||||
}
|
||||
if (debugOutput != null)
|
||||
debugOutput.println("<< "+line);
|
||||
debugOutput.println("<< " + line);
|
||||
if (line.length() < 4)
|
||||
throw new TorControlSyntaxError("Line (\""+line+"\") too short");
|
||||
String status = line.substring(0,3);
|
||||
throw new TorControlSyntaxError(
|
||||
"Line (\"" + line + "\") too short");
|
||||
String status = line.substring(0, 3);
|
||||
c = line.charAt(3);
|
||||
String msg = line.substring(4);
|
||||
String rest = null;
|
||||
@@ -163,7 +200,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
while (true) {
|
||||
line = input.readLine();
|
||||
if (debugOutput != null)
|
||||
debugOutput.print("<< "+line);
|
||||
debugOutput.print("<< " + line);
|
||||
if (line.equals("."))
|
||||
break;
|
||||
else if (line.startsWith("."))
|
||||
@@ -178,36 +215,61 @@ public class TorControlConnection implements TorControlCommands {
|
||||
return reply;
|
||||
}
|
||||
|
||||
protected synchronized List<ReplyLine> sendAndWaitForResponse(String s,
|
||||
protected List<ReplyLine> sendAndWaitForResponse(String s,
|
||||
String rest) throws IOException {
|
||||
if (parseThreadException != null) throw parseThreadException;
|
||||
LOG.info("Entering synchronized (connection)");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (connection)");
|
||||
LOG.info("Sending '" + s + "', '" + rest +
|
||||
"' and waiting for response");
|
||||
if (parseThreadException != null) {
|
||||
LOG.info("Throwing previously caught exception "
|
||||
+ parseThreadException);
|
||||
throw parseThreadException;
|
||||
}
|
||||
checkThread();
|
||||
Waiter w = new Waiter();
|
||||
LOG.info("Created waiter " + w.hashCode());
|
||||
if (debugOutput != null)
|
||||
debugOutput.print(">> "+s);
|
||||
debugOutput.print(">> " + s);
|
||||
LOG.info("Entering synchronized (waiters)");
|
||||
synchronized (waiters) {
|
||||
LOG.info("Entered synchronized (waiters)");
|
||||
output.write(s);
|
||||
if (rest != null)
|
||||
LOG.info("Wrote '" + s + "'");
|
||||
if (rest != null) {
|
||||
writeEscaped(rest);
|
||||
LOG.info("Wrote escaped '" + rest + "'");
|
||||
}
|
||||
output.flush();
|
||||
LOG.info("Flushed output");
|
||||
waiters.addLast(w);
|
||||
LOG.info("Added waiter, " + waiters.size() + " waiting");
|
||||
LOG.info("Leaving synchronized (waiters)");
|
||||
}
|
||||
List<ReplyLine> lst;
|
||||
try {
|
||||
LOG.info("Getting response from waiter " + w.hashCode());
|
||||
lst = w.getResponse();
|
||||
LOG.info("Got response from waiter " + w.hashCode() + ": " +
|
||||
lst);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new IOException("Interrupted");
|
||||
}
|
||||
for (Iterator<ReplyLine> i = lst.iterator(); i.hasNext(); ) {
|
||||
ReplyLine c = i.next();
|
||||
if (! c.status.startsWith("2"))
|
||||
throw new TorControlError("Error reply: "+c.msg);
|
||||
if (!c.status.startsWith("2"))
|
||||
throw new TorControlError("Error reply: " + c.msg);
|
||||
}
|
||||
LOG.info("Leaving synchronized (connection)");
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper: decode a CMD_EVENT command and dispatch it to our
|
||||
* EventHandler (if any). */
|
||||
/**
|
||||
* Helper: decode a CMD_EVENT command and dispatch it to our
|
||||
* EventHandler (if any).
|
||||
*/
|
||||
protected void handleEvent(ArrayList<ReplyLine> events) {
|
||||
if (handler == null)
|
||||
return;
|
||||
@@ -216,7 +278,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
ReplyLine line = i.next();
|
||||
int idx = line.msg.indexOf(' ');
|
||||
String tp = line.msg.substring(0, idx).toUpperCase();
|
||||
String rest = line.msg.substring(idx+1);
|
||||
String rest = line.msg.substring(idx + 1);
|
||||
if (tp.equals("CIRC")) {
|
||||
List<String> lst = Bytes.splitStr(null, rest);
|
||||
handler.circuitStatus(lst.get(1),
|
||||
@@ -253,7 +315,8 @@ public class TorControlConnection implements TorControlCommands {
|
||||
}
|
||||
|
||||
|
||||
/** Sets <b>w</b> as the PrintWriter for debugging output,
|
||||
/**
|
||||
* 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 "\<\<"
|
||||
@@ -262,7 +325,8 @@ public class TorControlConnection implements TorControlCommands {
|
||||
debugOutput = w;
|
||||
}
|
||||
|
||||
/** Sets <b>s</b> as the PrintStream for debugging output,
|
||||
/**
|
||||
* 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 "\<\<"
|
||||
@@ -271,9 +335,11 @@ public class TorControlConnection implements TorControlCommands {
|
||||
debugOutput = new PrintWriter(s, true);
|
||||
}
|
||||
|
||||
/** Set the EventHandler object that will be notified of any
|
||||
/**
|
||||
* Set the EventHandler object that will be notified of any
|
||||
* events Tor delivers to this connection. To make Tor send us
|
||||
* events, call setEvents(). */
|
||||
* events, call setEvents().
|
||||
*/
|
||||
public void setEventHandler(EventHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
@@ -283,14 +349,20 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* This is necessary to handle asynchronous events and synchronous
|
||||
* responses that arrive independantly over the same socket.
|
||||
*/
|
||||
public synchronized Thread launchThread(boolean daemon) {
|
||||
public Thread launchThread(boolean daemon) {
|
||||
LOG.info("Entering synchronized (connection)");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (connection)");
|
||||
ControlParseThread th = new ControlParseThread();
|
||||
LOG.info("Launching parse thread " + th.hashCode());
|
||||
if (daemon)
|
||||
th.setDaemon(true);
|
||||
th.start();
|
||||
this.thread = th;
|
||||
LOG.info("Leaving synchronized (connection)");
|
||||
return th;
|
||||
}
|
||||
}
|
||||
|
||||
protected class ControlParseThread extends Thread {
|
||||
|
||||
@@ -299,82 +371,109 @@ public class TorControlConnection implements TorControlCommands {
|
||||
try {
|
||||
react();
|
||||
} catch (IOException ex) {
|
||||
LOG.info("Parse thread " + hashCode()
|
||||
+ " caught exception " + ex);
|
||||
parseThreadException = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void checkThread() {
|
||||
protected void checkThread() {
|
||||
LOG.info("Entering synchronized (connection)");
|
||||
synchronized (this) {
|
||||
LOG.info("Entered synchronized (connection)");
|
||||
if (thread == null)
|
||||
launchThread(true);
|
||||
LOG.info("Leaving synchronized (connection)");
|
||||
}
|
||||
}
|
||||
|
||||
/** helper: implement the main background loop. */
|
||||
/**
|
||||
* helper: implement the main background loop.
|
||||
*/
|
||||
protected void react() throws IOException {
|
||||
while (true) {
|
||||
ArrayList<ReplyLine> lst = readReply();
|
||||
LOG.info("Read reply: " + lst);
|
||||
if (lst.isEmpty()) {
|
||||
// interrupted queued waiters, there won't be any response.
|
||||
LOG.info("Entering synchronized (waiters)");
|
||||
synchronized (waiters) {
|
||||
LOG.info("Entered synchronized (waiters)");
|
||||
if (!waiters.isEmpty()) {
|
||||
for (Waiter w : waiters) {
|
||||
LOG.info("Interrupting waiter " + w.hashCode());
|
||||
w.interrupt();
|
||||
}
|
||||
} else {
|
||||
LOG.info("No waiters");
|
||||
}
|
||||
LOG.info("Leaving synchronized (waiters)");
|
||||
}
|
||||
throw new IOException("Tor is no longer running");
|
||||
}
|
||||
if ((lst.get(0)).status.startsWith("6"))
|
||||
if ((lst.get(0)).status.startsWith("6")) {
|
||||
LOG.info("Reply is an event");
|
||||
handleEvent(lst);
|
||||
else {
|
||||
} else {
|
||||
LOG.info("Entering synchronized (waiters)");
|
||||
synchronized (waiters) {
|
||||
if (!waiters.isEmpty())
|
||||
{
|
||||
LOG.info("Entered synchronized (waiters)");
|
||||
if (!waiters.isEmpty()) {
|
||||
Waiter w;
|
||||
w = waiters.removeFirst();
|
||||
LOG.info("Setting response for waiter " + w.hashCode());
|
||||
w.setResponse(lst);
|
||||
} else {
|
||||
LOG.info("No waiters");
|
||||
}
|
||||
LOG.info("Leaving synchronized (waiters)");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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);
|
||||
List<String> lst = new ArrayList<>();
|
||||
lst.add(key + " " + value);
|
||||
setConf(lst);
|
||||
}
|
||||
|
||||
/** Change the values of the configuration options stored in kvMap. */
|
||||
/**
|
||||
* 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");
|
||||
List<String> lst = new ArrayList<>();
|
||||
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");
|
||||
}
|
||||
setConf(lst);
|
||||
}
|
||||
|
||||
/** Changes the values of the configuration options stored in
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -387,14 +486,15 @@ public class TorControlConnection implements TorControlCommands {
|
||||
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("=")
|
||||
.append(quote(kv.substring(i + 1)));
|
||||
}
|
||||
b.append("\r\n");
|
||||
sendAndWaitForResponse(b.toString(), null);
|
||||
}
|
||||
|
||||
/** Try to reset the values listed in the collection 'keys' to their
|
||||
/**
|
||||
* Try to reset the values listed in the collection 'keys' to their
|
||||
* default values.
|
||||
**/
|
||||
public void resetConf(Collection<String> keys) throws IOException {
|
||||
@@ -409,26 +509,30 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse(b.toString(), null);
|
||||
}
|
||||
|
||||
/** Return the value of the configuration option 'key' */
|
||||
/**
|
||||
* Return the value of the configuration option 'key'
|
||||
*/
|
||||
public List<ConfigEntry> getConf(String key) throws IOException {
|
||||
List<String> lst = new ArrayList<String>();
|
||||
List<String> lst = new ArrayList<>();
|
||||
lst.add(key);
|
||||
return getConf(lst);
|
||||
}
|
||||
|
||||
/** Requests the values of the configuration variables listed in <b>keys</b>.
|
||||
/**
|
||||
* Requests the values of the configuration variables listed in <b>keys</b>.
|
||||
* Results are returned as a list of ConfigEntry objects.
|
||||
*
|
||||
* <p>
|
||||
* If an option appears multiple times in the configuration, all of its
|
||||
* key-value pairs are returned in order.
|
||||
*
|
||||
* <p>
|
||||
* Some options are context-sensitive, and depend on other options with
|
||||
* different keywords. These cannot be fetched directly. Currently there
|
||||
* is only one such option: clients should use the "HiddenServiceOptions"
|
||||
* virtual keyword to get all HiddenServiceDir, HiddenServicePort,
|
||||
* HiddenServiceNodes, and HiddenServiceExcludeNodes option settings.
|
||||
*/
|
||||
public List<ConfigEntry> getConf(Collection<String> keys) throws IOException {
|
||||
public List<ConfigEntry> getConf(Collection<String> keys)
|
||||
throws IOException {
|
||||
StringBuffer sb = new StringBuffer("GETCONF");
|
||||
for (Iterator<String> it = keys.iterator(); it.hasNext(); ) {
|
||||
String key = it.next();
|
||||
@@ -436,24 +540,25 @@ public class TorControlConnection implements TorControlCommands {
|
||||
}
|
||||
sb.append("\r\n");
|
||||
List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null);
|
||||
List<ConfigEntry> result = new ArrayList<ConfigEntry>();
|
||||
List<ConfigEntry> result = new ArrayList<>();
|
||||
for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) {
|
||||
String kv = (it.next()).msg;
|
||||
int idx = kv.indexOf('=');
|
||||
if (idx >= 0)
|
||||
result.add(new ConfigEntry(kv.substring(0, idx),
|
||||
kv.substring(idx+1)));
|
||||
kv.substring(idx + 1)));
|
||||
else
|
||||
result.add(new ConfigEntry(kv));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Request that the server inform the client about interesting events.
|
||||
/**
|
||||
* 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"] .
|
||||
*
|
||||
* <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.
|
||||
*/
|
||||
@@ -466,21 +571,22 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse(sb.toString(), null);
|
||||
}
|
||||
|
||||
/** Authenticates the controller to the Tor server.
|
||||
*
|
||||
/**
|
||||
* 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>'
|
||||
* or by using the provided PasswordDigest class.
|
||||
@@ -492,13 +598,15 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse(cmd, null);
|
||||
}
|
||||
|
||||
/** Instructs the server to write out its configuration options into its torrc.
|
||||
/**
|
||||
* Instructs the server to write out its configuration options into its torrc.
|
||||
*/
|
||||
public void saveConf() throws IOException {
|
||||
sendAndWaitForResponse("SAVECONF\r\n", null);
|
||||
}
|
||||
|
||||
/** Sends a signal from the controller to the Tor server.
|
||||
/**
|
||||
* Sends a signal from the controller to the Tor server.
|
||||
* <b>signal</b> is one of the following Strings:
|
||||
* <ul>
|
||||
* <li>"RELOAD" or "HUP" : Reload config items, refetch directory</li>
|
||||
@@ -514,24 +622,30 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse(cmd, null);
|
||||
}
|
||||
|
||||
/** Send a signal to the Tor process to shut it down or halt it.
|
||||
* Does not wait for a response. */
|
||||
/**
|
||||
* Send a signal to the Tor process to shut it down or halt it.
|
||||
* Does not wait for a response.
|
||||
*/
|
||||
public void shutdownTor(String signal) throws IOException {
|
||||
String s = "SIGNAL " + signal + "\r\n";
|
||||
Waiter w = new Waiter();
|
||||
if (debugOutput != null)
|
||||
debugOutput.print(">> "+s);
|
||||
debugOutput.print(">> " + s);
|
||||
LOG.info("Entering synchronized (waiters)");
|
||||
synchronized (waiters) {
|
||||
LOG.info("Entered synchronized (waiters)");
|
||||
output.write(s);
|
||||
output.flush();
|
||||
LOG.info("Leaving synchronized (waiters)");
|
||||
}
|
||||
}
|
||||
|
||||
/** Tells the Tor server that future SOCKS requests for connections to a set of original
|
||||
/**
|
||||
* Tells the Tor server that future SOCKS requests for connections to a set of original
|
||||
* addresses should be replaced with connections to the specified replacement
|
||||
* addresses. Each element of <b>kvLines</b> is a String of the form
|
||||
* "old-address new-address". This function returns the new address mapping.
|
||||
*
|
||||
* <p>
|
||||
* The client may decline to provide a body for the original address, and
|
||||
* instead send a special null address ("0.0.0.0" for IPv4, "::0" for IPv6, or
|
||||
* "." for hostname), signifying that the server should choose the original
|
||||
@@ -539,56 +653,61 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* should ensure that it returns an element of address space that is unlikely
|
||||
* to be in actual use. If there is already an address mapped to the
|
||||
* destination address, the server may reuse that mapping.
|
||||
*
|
||||
* <p>
|
||||
* If the original address is already mapped to a different address, the old
|
||||
* mapping is removed. If the original address and the destination address
|
||||
* are the same, the server removes any mapping in place for the original
|
||||
* address.
|
||||
*
|
||||
* <p>
|
||||
* Mappings set by the controller last until the Tor process exits:
|
||||
* they never expire. If the controller wants the mapping to last only
|
||||
* a certain time, then it must explicitly un-map the address when that
|
||||
* time has elapsed.
|
||||
*/
|
||||
public Map<String,String> mapAddresses(Collection<String> kvLines) throws IOException {
|
||||
public Map<String, String> mapAddresses(Collection<String> kvLines)
|
||||
throws IOException {
|
||||
StringBuffer sb = new StringBuffer("MAPADDRESS");
|
||||
for (Iterator<String> it = kvLines.iterator(); it.hasNext(); ) {
|
||||
String kv = it.next();
|
||||
int i = kv.indexOf(' ');
|
||||
sb.append(" ").append(kv.substring(0,i)).append("=")
|
||||
.append(quote(kv.substring(i+1)));
|
||||
sb.append(" ").append(kv.substring(0, i)).append("=")
|
||||
.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>();
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) {
|
||||
String kv = (it.next()).msg;
|
||||
int idx = kv.indexOf('=');
|
||||
result.put(kv.substring(0, idx),
|
||||
kv.substring(idx+1));
|
||||
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();
|
||||
kvList.add(e.getKey()+" "+e.getValue());
|
||||
public Map<String, String> mapAddresses(Map<String, String> addresses)
|
||||
throws IOException {
|
||||
List<String> kvList = new ArrayList<>();
|
||||
for (Iterator<Map.Entry<String, String>> it =
|
||||
addresses.entrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry<String, String> e = it.next();
|
||||
kvList.add(e.getKey() + " " + e.getValue());
|
||||
}
|
||||
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);
|
||||
public String mapAddress(String fromAddr, String toAddr)
|
||||
throws IOException {
|
||||
List<String> lst = new ArrayList<>();
|
||||
lst.add(fromAddr + " " + toAddr + "\n");
|
||||
Map<String, String> m = mapAddresses(lst);
|
||||
return m.get(fromAddr);
|
||||
}
|
||||
|
||||
/** Queries the Tor server for keyed values that are not stored in the torrc
|
||||
/**
|
||||
* 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
|
||||
@@ -616,25 +735,26 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* form: "ServerID ORStatus"</li>
|
||||
* </ul>
|
||||
*/
|
||||
public Map<String,String> getInfo(Collection<String> keys) throws IOException {
|
||||
public Map<String, String> getInfo(Collection<String> keys)
|
||||
throws IOException {
|
||||
StringBuffer sb = new StringBuffer("GETINFO");
|
||||
for (Iterator<String> it = keys.iterator(); it.hasNext(); ) {
|
||||
sb.append(" ").append(it.next());
|
||||
}
|
||||
sb.append("\r\n");
|
||||
List<ReplyLine> lst = sendAndWaitForResponse(sb.toString(), null);
|
||||
Map<String,String> m = new HashMap<String,String>();
|
||||
Map<String, String> m = new HashMap<>();
|
||||
for (Iterator<ReplyLine> it = lst.iterator(); it.hasNext(); ) {
|
||||
ReplyLine line = it.next();
|
||||
int idx = line.msg.indexOf('=');
|
||||
if (idx<0)
|
||||
if (idx < 0)
|
||||
break;
|
||||
String k = line.msg.substring(0,idx);
|
||||
String k = line.msg.substring(0, idx);
|
||||
String v;
|
||||
if (line.rest != null) {
|
||||
v = line.rest;
|
||||
} else {
|
||||
v = line.msg.substring(idx+1);
|
||||
v = line.msg.substring(idx + 1);
|
||||
}
|
||||
m.put(k, v);
|
||||
}
|
||||
@@ -642,41 +762,44 @@ public class TorControlConnection implements TorControlCommands {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Return the value of the information field 'key' */
|
||||
/**
|
||||
* Return the value of the information field 'key'
|
||||
*/
|
||||
public String getInfo(String key) throws IOException {
|
||||
List<String> lst = new ArrayList<String>();
|
||||
List<String> lst = new ArrayList<>();
|
||||
lst.add(key);
|
||||
Map<String,String> m = getInfo(lst);
|
||||
Map<String, String> m = getInfo(lst);
|
||||
return m.get(key);
|
||||
}
|
||||
|
||||
/** An extendCircuit request takes one of two forms: either the <b>circID</b> is zero, in
|
||||
/**
|
||||
* An extendCircuit request takes one of two forms: either the <b>circID</b> is zero, in
|
||||
* which case it is a request for the server to build a new circuit according
|
||||
* to the specified path, or the <b>circID</b> is nonzero, in which case it is a
|
||||
* request for the server to extend an existing circuit with that ID according
|
||||
* to the specified <b>path</b>.
|
||||
*
|
||||
* <p>
|
||||
* If successful, returns the Circuit ID of the (maybe newly created) circuit.
|
||||
*/
|
||||
public String extendCircuit(String circID, String path) throws IOException {
|
||||
List<ReplyLine> lst = sendAndWaitForResponse(
|
||||
"EXTENDCIRCUIT "+circID+" "+path+"\r\n", null);
|
||||
"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
|
||||
/**
|
||||
* 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
|
||||
@@ -685,34 +808,41 @@ public class TorControlConnection implements TorControlCommands {
|
||||
*/
|
||||
public void attachStream(String streamID, String circID)
|
||||
throws IOException {
|
||||
sendAndWaitForResponse("ATTACHSTREAM "+streamID+" "+circID+"\r\n", null);
|
||||
sendAndWaitForResponse(
|
||||
"ATTACHSTREAM " + streamID + " " + circID + "\r\n", null);
|
||||
}
|
||||
|
||||
/** Tells Tor about the server descriptor in <b>desc</b>.
|
||||
*
|
||||
/**
|
||||
* Tells Tor about the server descriptor in <b>desc</b>.
|
||||
* <p>
|
||||
* The descriptor, when parsed, must contain a number of well-specified
|
||||
* fields, including fields for its nickname and identity.
|
||||
*/
|
||||
// More documentation here on format of desc?
|
||||
// No need for return value? control-spec.txt says reply is merely "250 OK" on success...
|
||||
public String postDescriptor(String desc) throws IOException {
|
||||
List<ReplyLine> lst = sendAndWaitForResponse("+POSTDESCRIPTOR\r\n", desc);
|
||||
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>
|
||||
/**
|
||||
* Tells Tor to change the exit address of the stream identified by <b>streamID</b>
|
||||
* to <b>address</b>. No remapping is performed on the new provided address.
|
||||
*
|
||||
* <p>
|
||||
* To be sure that the modified address will be used, this event must be sent
|
||||
* after a new stream event is received, and before attaching this stream to
|
||||
* a circuit.
|
||||
*/
|
||||
public void redirectStream(String streamID, String address) throws IOException {
|
||||
sendAndWaitForResponse("REDIRECTSTREAM "+streamID+" "+address+"\r\n",
|
||||
public void redirectStream(String streamID, String address)
|
||||
throws IOException {
|
||||
sendAndWaitForResponse(
|
||||
"REDIRECTSTREAM " + streamID + " " + address + "\r\n",
|
||||
null);
|
||||
}
|
||||
|
||||
/** Tells Tor to close the stream identified by <b>streamID</b>.
|
||||
/**
|
||||
* Tells Tor to close the stream identified by <b>streamID</b>.
|
||||
* <b>reason</b> should be one of the Tor RELAY_END reasons given in tor-spec.txt, as a decimal:
|
||||
* <ul>
|
||||
* <li>1 -- REASON_MISC (catch-all for unlisted reasons)</li>
|
||||
@@ -729,23 +859,27 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <li>12 -- REASON_CONNRESET (Connection was unexpectedly reset)</li>
|
||||
* <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of Tor protocol violations)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Tor may hold the stream open for a while to flush any data that is pending.
|
||||
*/
|
||||
public void closeStream(String streamID, byte reason)
|
||||
throws IOException {
|
||||
sendAndWaitForResponse("CLOSESTREAM "+streamID+" "+reason+"\r\n",null);
|
||||
sendAndWaitForResponse(
|
||||
"CLOSESTREAM " + streamID + " " + reason + "\r\n", null);
|
||||
}
|
||||
|
||||
/** Tells Tor to close the circuit identified by <b>circID</b>.
|
||||
/**
|
||||
* Tells Tor to close the circuit identified by <b>circID</b>.
|
||||
* If <b>ifUnused</b> is true, do not close the circuit unless it is unused.
|
||||
*/
|
||||
public void closeCircuit(String circID, boolean ifUnused) throws IOException {
|
||||
sendAndWaitForResponse("CLOSECIRCUIT "+circID+
|
||||
(ifUnused?" IFUNUSED":"")+"\r\n", null);
|
||||
public void closeCircuit(String circID, boolean ifUnused)
|
||||
throws IOException {
|
||||
sendAndWaitForResponse("CLOSECIRCUIT " + circID +
|
||||
(ifUnused ? " IFUNUSED" : "") + "\r\n", null);
|
||||
}
|
||||
|
||||
/** Tells Tor to exit when this control connection is closed. This command
|
||||
/**
|
||||
* 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 {
|
||||
@@ -758,7 +892,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <p/>
|
||||
* ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
*/
|
||||
public Map<String,String> addOnion(Map<Integer,String> portLines)
|
||||
public Map<String, String> addOnion(Map<Integer, String> portLines)
|
||||
throws IOException {
|
||||
return addOnion("NEW:BEST", portLines, null);
|
||||
}
|
||||
@@ -769,7 +903,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <p/>
|
||||
* ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
*/
|
||||
public Map<String,String> addOnion(Map<Integer,String> portLines,
|
||||
public Map<String, String> addOnion(Map<Integer, String> portLines,
|
||||
boolean ephemeral, boolean detach)
|
||||
throws IOException {
|
||||
return addOnion("NEW:BEST", portLines, ephemeral, detach);
|
||||
@@ -780,8 +914,8 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <p/>
|
||||
* ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
*/
|
||||
public Map<String,String> addOnion(String privKey,
|
||||
Map<Integer,String> portLines)
|
||||
public Map<String, String> addOnion(String privKey,
|
||||
Map<Integer, String> portLines)
|
||||
throws IOException {
|
||||
return addOnion(privKey, portLines, null);
|
||||
}
|
||||
@@ -791,11 +925,11 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <p/>
|
||||
* ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
*/
|
||||
public Map<String,String> addOnion(String privKey,
|
||||
Map<Integer,String> portLines,
|
||||
public Map<String, String> addOnion(String privKey,
|
||||
Map<Integer, String> portLines,
|
||||
boolean ephemeral, boolean detach)
|
||||
throws IOException {
|
||||
List<String> flags = new ArrayList<String>();
|
||||
List<String> flags = new ArrayList<>();
|
||||
if (ephemeral)
|
||||
flags.add("DiscardPK");
|
||||
if (detach)
|
||||
@@ -808,14 +942,15 @@ public class TorControlConnection implements TorControlCommands {
|
||||
* <p/>
|
||||
* ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
*/
|
||||
public Map<String,String> addOnion(String privKey,
|
||||
Map<Integer,String> portLines,
|
||||
public Map<String, String> addOnion(String privKey,
|
||||
Map<Integer, String> portLines,
|
||||
List<String> flags)
|
||||
throws IOException {
|
||||
if (privKey.indexOf(':') < 0)
|
||||
throw new IllegalArgumentException("Invalid privKey");
|
||||
if (portLines == null || portLines.size() < 1)
|
||||
throw new IllegalArgumentException("Must provide at least one port line");
|
||||
throw new IllegalArgumentException(
|
||||
"Must provide at least one port line");
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("ADD_ONION ").append(privKey);
|
||||
if (flags != null && flags.size() > 0) {
|
||||
@@ -826,7 +961,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
separator = ",";
|
||||
}
|
||||
}
|
||||
for (Map.Entry<Integer,String> portLine : portLines.entrySet()) {
|
||||
for (Map.Entry<Integer, String> portLine : portLines.entrySet()) {
|
||||
int virtPort = portLine.getKey();
|
||||
String target = portLine.getValue();
|
||||
b.append(" Port=").append(virtPort);
|
||||
@@ -835,7 +970,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
}
|
||||
b.append("\r\n");
|
||||
List<ReplyLine> lst = sendAndWaitForResponse(b.toString(), null);
|
||||
Map<String,String> ret = new HashMap<String,String>();
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
ret.put(HS_ADDRESS, (lst.get(0)).msg.split("=", 2)[1]);
|
||||
if (lst.size() > 2)
|
||||
ret.put(HS_PRIVKEY, (lst.get(1)).msg.split("=", 2)[1]);
|
||||
@@ -852,7 +987,8 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse("DEL_ONION " + hostname + "\r\n", null);
|
||||
}
|
||||
|
||||
/** Tells Tor to forget any cached client state relating to the hidden
|
||||
/**
|
||||
* Tells Tor to forget any cached client state relating to the hidden
|
||||
* service with the given hostname (excluding the .onion extension).
|
||||
*/
|
||||
public void forgetHiddenService(String hostname) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user