Add an absurd amount of logging.

This commit is contained in:
akwizgran
2019-04-02 16:52:28 +01:00
parent ea006d21f9
commit 5751958eaf

View File

@@ -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 {
@@ -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();
@@ -153,7 +189,8 @@ public class TorControlConnection implements TorControlCommands {
if (debugOutput != null)
debugOutput.println("<< " + line);
if (line.length() < 4)
throw new TorControlSyntaxError("Line (\""+line+"\") too short");
throw new TorControlSyntaxError(
"Line (\"" + line + "\") too short");
String status = line.substring(0, 3);
c = line.charAt(3);
String msg = line.substring(4);
@@ -178,23 +215,44 @@ 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);
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");
}
@@ -203,11 +261,15 @@ public class TorControlConnection implements TorControlCommands {
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;
@@ -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>();
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(); ) {
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.
*/
@@ -394,7 +493,8 @@ public class TorControlConnection implements TorControlCommands {
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,7 +540,7 @@ 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('=');
@@ -449,11 +553,12 @@ public class TorControlConnection implements TorControlCommands {
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);
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,18 +653,19 @@ 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();
@@ -560,7 +675,7 @@ public class TorControlConnection implements TorControlCommands {
}
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('=');
@@ -570,25 +685,29 @@ public class TorControlConnection implements TorControlCommands {
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(); ) {
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>();
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,14 +735,15 @@ 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('=');
@@ -642,21 +762,23 @@ 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);
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 {
@@ -665,18 +787,19 @@ public class TorControlConnection implements TorControlCommands {
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 {
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 {
@@ -795,7 +929,7 @@ public class TorControlConnection implements TorControlCommands {
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)
@@ -815,7 +949,8 @@ public class TorControlConnection implements TorControlCommands {
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) {
@@ -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 {