more cleanup, and update of classes for installation

This commit is contained in:
n8fr8 2017-11-17 10:58:42 -05:00
parent 17f03d1f42
commit f8dbfacdb3
42 changed files with 14 additions and 4755 deletions

View File

@ -1,114 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.util.Arrays;
import java.util.List;
/**
* Static class to do bytewise structure manipulation in Java.
*/
/* XXXX There must be a better way to do most of this.
* XXXX The string logic here uses default encoding, which is stupid.
*/
final class Bytes {
/** Write the two-byte value in 's' into the byte array 'ba', starting at
* the index 'pos'. */
public static void setU16(byte[] ba, int pos, short s) {
ba[pos] = (byte)((s >> 8) & 0xff);
ba[pos+1] = (byte)((s ) & 0xff);
}
/** Write the four-byte value in 'i' into the byte array 'ba', starting at
* the index 'pos'. */
public static void setU32(byte[] ba, int pos, int i) {
ba[pos] = (byte)((i >> 24) & 0xff);
ba[pos+1] = (byte)((i >> 16) & 0xff);
ba[pos+2] = (byte)((i >> 8) & 0xff);
ba[pos+3] = (byte)((i ) & 0xff);
}
/** Return the four-byte value starting at index 'pos' within 'ba' */
public static int getU32(byte[] ba, int pos) {
return
((ba[pos ]&0xff)<<24) |
((ba[pos+1]&0xff)<<16) |
((ba[pos+2]&0xff)<< 8) |
((ba[pos+3]&0xff));
}
public static String getU32S(byte[] ba, int pos) {
return String.valueOf( (getU32(ba,pos))&0xffffffffL );
}
/** Return the two-byte value starting at index 'pos' within 'ba' */
public static int getU16(byte[] ba, int pos) {
return
((ba[pos ]&0xff)<<8) |
((ba[pos+1]&0xff));
}
/** Return the string starting at position 'pos' of ba and extending
* until a zero byte or the end of the string. */
public static String getNulTerminatedStr(byte[] ba, int pos) {
int len, maxlen = ba.length-pos;
for (len=0; len<maxlen; ++len) {
if (ba[pos+len] == 0)
break;
}
return new String(ba, pos, len);
}
/**
* Read bytes from 'ba' starting at 'pos', dividing them into strings
* along the character in 'split' and writing them into 'lst'
*/
public static void splitStr(List<String> lst, byte[] ba, int pos, byte split) {
while (pos < ba.length && ba[pos] != 0) {
int len;
for (len=0; pos+len < ba.length; ++len) {
if (ba[pos+len] == 0 || ba[pos+len] == split)
break;
}
if (len>0)
lst.add(new String(ba, pos, len));
pos += len;
if (ba[pos] == split)
++pos;
}
}
/**
* Read bytes from 'ba' starting at 'pos', dividing them into strings
* along the character in 'split' and writing them into 'lst'
*/
public static List<String> splitStr(List<String> lst, String str) {
// split string on spaces, include trailing/leading
String[] tokenArray = str.split(" ", -1);
if (lst == null) {
lst = Arrays.asList( tokenArray );
} else {
lst.addAll( Arrays.asList( tokenArray ) );
}
return lst;
}
private static final char[] NYBBLES = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
public static final String hex(byte[] ba) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < ba.length; ++i) {
int b = (ba[i]) & 0xff;
buf.append(NYBBLES[b >> 4]);
buf.append(NYBBLES[b&0x0f]);
}
return buf.toString();
}
private Bytes() {};
}

View File

@ -1,20 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/** A single key-value pair from Tor's configuration. */
public class ConfigEntry {
public ConfigEntry(String k, String v) {
key = k;
value = v;
is_default = false;
}
public ConfigEntry(String k) {
key = k;
value = "";
is_default = true;
}
public final String key;
public final String value;
public final boolean is_default;
}

View File

@ -1,75 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/**
* Abstract interface whose methods are invoked when Tor sends us an event.
*
* @see TorControlConnection#setEventHandler
* @see TorControlConnection#setEvents
*/
public interface EventHandler {
/**
* Invoked when a circuit's status has changed.
* Possible values for <b>status</b> are:
* <ul>
* <li>"LAUNCHED" : circuit ID assigned to new circuit</li>
* <li>"BUILT" : all hops finished, can now accept streams</li>
* <li>"EXTENDED" : one more hop has been completed</li>
* <li>"FAILED" : circuit closed (was not built)</li>
* <li>"CLOSED" : circuit closed (was built)</li>
* </ul>
*
* <b>circID</b> is the alphanumeric identifier of the affected circuit,
* and <b>path</b> is a comma-separated list of alphanumeric ServerIDs.
*/
public void circuitStatus(String status, String circID, String path);
/**
* Invoked when a stream's status has changed.
* Possible values for <b>status</b> are:
* <ul>
* <li>"NEW" : New request to connect</li>
* <li>"NEWRESOLVE" : New request to resolve an address</li>
* <li>"SENTCONNECT" : Sent a connect cell along a circuit</li>
* <li>"SENTRESOLVE" : Sent a resolve cell along a circuit</li>
* <li>"SUCCEEDED" : Received a reply; stream established</li>
* <li>"FAILED" : Stream failed and not retriable.</li>
* <li>"CLOSED" : Stream closed</li>
* <li>"DETACHED" : Detached from circuit; still retriable.</li>
* </ul>
*
* <b>streamID</b> is the alphanumeric identifier of the affected stream,
* and its <b>target</b> is specified as address:port.
*/
public void streamStatus(String status, String streamID, String target);
/**
* Invoked when the status of a connection to an OR has changed.
* Possible values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | "CLOSED"].
* <b>orName</b> is the alphanumeric identifier of the OR affected.
*/
public void orConnStatus(String status, String orName);
/**
* Invoked once per second. <b>read</b> and <b>written</b> are
* the number of bytes read and written, respectively, in
* the last second.
*/
public void bandwidthUsed(long read, long written);
/**
* Invoked whenever Tor learns about new ORs. The <b>orList</b> object
* contains the alphanumeric ServerIDs associated with the new ORs.
*/
public void newDescriptors(java.util.List<String> orList);
/**
* Invoked when Tor logs a message.
* <b>severity</b> is one of ["DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERR"],
* and <b>msg</b> is the message string.
*/
public void message(String severity, String msg);
/**
* Invoked when an unspecified message is received.
* <type> is the message type, and <msg> is the message string.
*/
public void unrecognized(String type, String msg);
}

View File

@ -1,18 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/**
* Implementation of EventHandler that ignores all events. Useful
* when you only want to override one method.
*/
public class NullEventHandler implements EventHandler {
public void circuitStatus(String status, String circID, String path) {}
public void streamStatus(String status, String streamID, String target) {}
public void orConnStatus(String status, String orName) {}
public void bandwidthUsed(long read, long written) {}
public void newDescriptors(java.util.List<String> orList) {}
public void message(String severity, String msg) {}
public void unrecognized(String type, String msg) {}
}

View File

@ -1,98 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* A hashed digest of a secret password (used to set control connection
* security.)
*
* For the actual hashing algorithm, see RFC2440's secret-to-key conversion.
*/
public class PasswordDigest {
private final byte[] secret;
private final String hashedKey;
/** Return a new password digest with a random secret and salt. */
public static PasswordDigest generateDigest() {
byte[] secret = new byte[20];
SecureRandom rng = new SecureRandom();
rng.nextBytes(secret);
return new PasswordDigest(secret);
}
/** Construct a new password digest with a given secret and random salt */
public PasswordDigest(byte[] secret) {
this(secret, null);
}
/** Construct a new password digest with a given secret and random salt.
* Note that the 9th byte of the specifier determines the number of hash
* iterations as in RFC2440.
*/
public PasswordDigest(byte[] secret, byte[] specifier) {
this.secret = secret.clone();
if (specifier == null) {
specifier = new byte[9];
SecureRandom rng = new SecureRandom();
rng.nextBytes(specifier);
specifier[8] = 96;
}
hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier));
}
/** Return the secret used to generate this password hash.
*/
public byte[] getSecret() {
return secret.clone();
}
/** Return the hashed password in the format used by Tor. */
public String getHashedPassword() {
return hashedKey;
}
/** Parameter used by RFC2440's s2k algorithm. */
private static final int EXPBIAS = 6;
/** Implement rfc2440 s2k */
public static byte[] secretToKey(byte[] secret, byte[] specifier) {
MessageDigest d;
try {
d = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("Can't run without sha-1.");
}
int c = (specifier[8])&0xff;
int count = (16 + (c&15)) << ((c>>4) + EXPBIAS);
byte[] tmp = new byte[8+secret.length];
System.arraycopy(specifier, 0, tmp, 0, 8);
System.arraycopy(secret, 0, tmp, 8, secret.length);
while (count > 0) {
if (count >= tmp.length) {
d.update(tmp);
count -= tmp.length;
} else {
d.update(tmp, 0, count);
count = 0;
}
}
byte[] key = new byte[20+9];
System.arraycopy(d.digest(), 0, key, 9, 20);
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);
}
}

View File

@ -1,4 +0,0 @@
We broke the version detection stuff in Tor 0.1.2.16 / 0.2.0.4-alpha.
Somebody should rip out the v0 control protocol stuff from here, and
it should start working again. -RD

View File

@ -1,148 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/** Interface defining constants used by the Tor controller protocol.
*/
// XXXX Take documentation for these from control-spec.txt
public interface TorControlCommands {
public static final short CMD_ERROR = 0x0000;
public static final short CMD_DONE = 0x0001;
public static final short CMD_SETCONF = 0x0002;
public static final short CMD_GETCONF = 0x0003;
public static final short CMD_CONFVALUE = 0x0004;
public static final short CMD_SETEVENTS = 0x0005;
public static final short CMD_EVENT = 0x0006;
public static final short CMD_AUTH = 0x0007;
public static final short CMD_SAVECONF = 0x0008;
public static final short CMD_SIGNAL = 0x0009;
public static final short CMD_MAPADDRESS = 0x000A;
public static final short CMD_GETINFO = 0x000B;
public static final short CMD_INFOVALUE = 0x000C;
public static final short CMD_EXTENDCIRCUIT = 0x000D;
public static final short CMD_ATTACHSTREAM = 0x000E;
public static final short CMD_POSTDESCRIPTOR = 0x000F;
public static final short CMD_FRAGMENTHEADER = 0x0010;
public static final short CMD_FRAGMENT = 0x0011;
public static final short CMD_REDIRECTSTREAM = 0x0012;
public static final short CMD_CLOSESTREAM = 0x0013;
public static final short CMD_CLOSECIRCUIT = 0x0014;
public static final String[] CMD_NAMES = {
"ERROR",
"DONE",
"SETCONF",
"GETCONF",
"CONFVALUE",
"SETEVENTS",
"EVENT",
"AUTH",
"SAVECONF",
"SIGNAL",
"MAPADDRESS",
"GETINFO",
"INFOVALUE",
"EXTENDCIRCUIT",
"ATTACHSTREAM",
"POSTDESCRIPTOR",
"FRAGMENTHEADER",
"FRAGMENT",
"REDIRECTSTREAM",
"CLOSESTREAM",
"CLOSECIRCUIT",
};
public static final short EVENT_CIRCSTATUS = 0x0001;
public static final short EVENT_STREAMSTATUS = 0x0002;
public static final short EVENT_ORCONNSTATUS = 0x0003;
public static final short EVENT_BANDWIDTH = 0x0004;
public static final short EVENT_NEWDESCRIPTOR = 0x0006;
public static final short EVENT_MSG_DEBUG = 0x0007;
public static final short EVENT_MSG_INFO = 0x0008;
public static final short EVENT_MSG_NOTICE = 0x0009;
public static final short EVENT_MSG_WARN = 0x000A;
public static final short EVENT_MSG_ERROR = 0x000B;
public static final String[] EVENT_NAMES = {
"(0)",
"CIRC",
"STREAM",
"ORCONN",
"BW",
"OLDLOG",
"NEWDESC",
"DEBUG",
"INFO",
"NOTICE",
"WARN",
"ERR",
};
public static final byte CIRC_STATUS_LAUNCHED = 0x01;
public static final byte CIRC_STATUS_BUILT = 0x02;
public static final byte CIRC_STATUS_EXTENDED = 0x03;
public static final byte CIRC_STATUS_FAILED = 0x04;
public static final byte CIRC_STATUS_CLOSED = 0x05;
public static final String[] CIRC_STATUS_NAMES = {
"LAUNCHED",
"BUILT",
"EXTENDED",
"FAILED",
"CLOSED",
};
public static final byte STREAM_STATUS_SENT_CONNECT = 0x00;
public static final byte STREAM_STATUS_SENT_RESOLVE = 0x01;
public static final byte STREAM_STATUS_SUCCEEDED = 0x02;
public static final byte STREAM_STATUS_FAILED = 0x03;
public static final byte STREAM_STATUS_CLOSED = 0x04;
public static final byte STREAM_STATUS_NEW_CONNECT = 0x05;
public static final byte STREAM_STATUS_NEW_RESOLVE = 0x06;
public static final byte STREAM_STATUS_DETACHED = 0x07;
public static final String[] STREAM_STATUS_NAMES = {
"SENT_CONNECT",
"SENT_RESOLVE",
"SUCCEEDED",
"FAILED",
"CLOSED",
"NEW_CONNECT",
"NEW_RESOLVE",
"DETACHED"
};
public static final byte OR_CONN_STATUS_LAUNCHED = 0x00;
public static final byte OR_CONN_STATUS_CONNECTED = 0x01;
public static final byte OR_CONN_STATUS_FAILED = 0x02;
public static final byte OR_CONN_STATUS_CLOSED = 0x03;
public static final String[] OR_CONN_STATUS_NAMES = {
"LAUNCHED","CONNECTED","FAILED","CLOSED"
};
public static final byte SIGNAL_HUP = 0x01;
public static final byte SIGNAL_INT = 0x02;
public static final byte SIGNAL_USR1 = 0x0A;
public static final byte SIGNAL_USR2 = 0x0C;
public static final byte SIGNAL_TERM = 0x0F;
public static final String ERROR_MSGS[] = {
"Unspecified error",
"Internal error",
"Unrecognized message type",
"Syntax error",
"Unrecognized configuration key",
"Invalid configuration value",
"Unrecognized byte code",
"Unauthorized",
"Failed authentication attempt",
"Resource exhausted",
"No such stream",
"No such circuit",
"No such OR",
};
}

View File

@ -1,732 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.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;
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 {
private final LinkedList<Waiter> waiters;
private final BufferedReader input;
private final Writer output;
private ControlParseThread thread; // Locking: this
private volatile EventHandler handler;
private volatile PrintWriter debugOutput;
private volatile IOException parseThreadException;
static class Waiter {
List<ReplyLine> response; // Locking: this
synchronized List<ReplyLine> getResponse() throws InterruptedException {
while (response == null) {
wait();
}
return response;
}
synchronized void setResponse(List<ReplyLine> response) {
this.response = response;
notifyAll();
}
}
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;
}
}
/** 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(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(InputStream i, OutputStream o) {
this(new InputStreamReader(i), new OutputStreamWriter(o));
}
public TorControlConnection(Reader i, Writer o) {
this.output = o;
if (i instanceof BufferedReader)
this.input = (BufferedReader) i;
else
this.input = new BufferedReader(i);
this.waiters = new LinkedList<Waiter>();
}
protected final void writeEscaped(String s) throws IOException {
StringTokenizer st = new StringTokenizer(s, "\n");
while (st.hasMoreTokens()) {
String line = st.nextToken();
if (line.startsWith("."))
line = "."+line;
if (line.endsWith("\r"))
line += "\n";
else
line += "\r\n";
if (debugOutput != null)
debugOutput.print(">> "+line);
output.write(line);
}
output.write(".\r\n");
if (debugOutput != null)
debugOutput.print(">> .\n");
}
protected static final String quote(String s) {
StringBuffer sb = new StringBuffer("\"");
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
switch (c)
{
case '\r':
case '\n':
case '\\':
case '\"':
sb.append('\\');
}
sb.append(c);
}
sb.append('\"');
return sb.toString();
}
protected final ArrayList<ReplyLine> readReply() throws IOException {
ArrayList<ReplyLine> reply = new ArrayList<ReplyLine>();
char c;
do {
String line = input.readLine();
if (line == null) {
// if line is null, the end of the stream has been reached, i.e.
// the connection to Tor has been closed!
if (reply.isEmpty()) {
// nothing received so far, can exit cleanly
return reply;
}
// received half of a reply before the connection broke down
throw new TorControlSyntaxError("Connection to Tor " +
" broke down while receiving reply!");
}
if (debugOutput != null)
debugOutput.println("<< "+line);
if (line.length() < 4)
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;
if (c == '+') {
StringBuffer data = new StringBuffer();
while (true) {
line = input.readLine();
if (debugOutput != null)
debugOutput.print("<< "+line);
if (line.equals("."))
break;
else if (line.startsWith("."))
line = line.substring(1);
data.append(line).append('\n');
}
rest = data.toString();
}
reply.add(new ReplyLine(status, msg, rest));
} while (c != ' ');
return reply;
}
protected synchronized List<ReplyLine> sendAndWaitForResponse(String s,
String rest) throws IOException {
if(parseThreadException != null) throw parseThreadException;
checkThread();
Waiter w = new Waiter();
if (debugOutput != null)
debugOutput.print(">> "+s);
synchronized (waiters) {
output.write(s);
if (rest != null)
writeEscaped(rest);
output.flush();
waiters.addLast(w);
}
List<ReplyLine> lst;
try {
lst = w.getResponse();
} 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);
}
return lst;
}
/** Helper: decode a CMD_EVENT command and dispatch it to our
* EventHandler (if any). */
protected void handleEvent(ArrayList<ReplyLine> events) {
if (handler == null)
return;
for (Iterator<ReplyLine> i = events.iterator(); i.hasNext(); ) {
ReplyLine line = i.next();
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() < 3 ? ""
: lst.get(2));
} else if (tp.equals("STREAM")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.streamStatus(lst.get(1),
lst.get(0),
lst.get(3));
// XXXX circID.
} else if (tp.equals("ORCONN")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.orConnStatus(lst.get(1), lst.get(0));
} else if (tp.equals("BW")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.bandwidthUsed(Integer.parseInt(lst.get(0)),
Integer.parseInt(lst.get(1)));
} else if (tp.equals("NEWDESC")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.newDescriptors(lst);
} else if (tp.equals("DEBUG") ||
tp.equals("INFO") ||
tp.equals("NOTICE") ||
tp.equals("WARN") ||
tp.equals("ERR")) {
handler.message(tp, rest);
} else {
handler.unrecognized(tp, rest);
}
}
}
/** 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 "\<\<"
*/
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 "\<\<"
*/
public void setDebugging(PrintStream s) {
debugOutput = new PrintWriter(s, true);
}
/** Set the EventHandler object that will be notified of any
* events Tor delivers to this connection. To make Tor send us
* events, call setEvents(). */
public void setEventHandler(EventHandler handler) {
this.handler = handler;
}
/**
* Start a thread to react to Tor's responses in the background.
* This is necessary to handle asynchronous events and synchronous
* responses that arrive independantly over the same socket.
*/
public synchronized Thread launchThread(boolean daemon) {
ControlParseThread th = new ControlParseThread();
if (daemon)
th.setDaemon(true);
th.start();
this.thread = th;
return th;
}
protected class ControlParseThread extends Thread {
@Override
public void run() {
try {
react();
} catch (IOException ex) {
parseThreadException = ex;
}
}
}
protected synchronized void checkThread() {
if (thread == null)
launchThread(true);
}
/** helper: implement the main background loop. */
protected void react() throws IOException {
while (true) {
ArrayList<ReplyLine> lst = readReply();
if (lst.isEmpty()) {
// connection has been closed remotely! end the loop!
return;
}
if ((lst.get(0)).status.startsWith("6"))
handleEvent(lst);
else {
synchronized (waiters) {
if (!waiters.isEmpty())
{
Waiter w;
w = waiters.removeFirst();
w.setResponse(lst);
}
}
}
}
}
/** 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);
}
/** 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");
}
setConf(lst);
}
/** 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();
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("\r\n");
sendAndWaitForResponse(b.toString(), null);
}
/** Try to reset the values listed in the collection 'keys' to their
* default values.
**/
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();
b.append(" ").append(key);
}
b.append("\r\n");
sendAndWaitForResponse(b.toString(), null);
}
/** Return the value of the configuration option 'key' */
public List<ConfigEntry> getConf(String key) throws IOException {
List<String> lst = new ArrayList<String>();
lst.add(key);
return getConf(lst);
}
/** 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"
* virtual keyword to get all HiddenServiceDir, HiddenServicePort,
* 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();
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;
int idx = kv.indexOf('=');
if (idx >= 0)
result.add(new ConfigEntry(kv.substring(0, idx),
kv.substring(idx+1)));
else
result.add(new ConfigEntry(kv));
}
return result;
}
/** Request that the server inform the client about interesting events.
* Each element of <b>events</b> is one of the following Strings:
* ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" |
* "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] .
*
* 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());
}
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]).
*
* 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>.
*
* 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>'
* 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.
*/
public void authenticate(byte[] auth) throws IOException {
String cmd = "AUTHENTICATE " + Bytes.hex(auth) + "\r\n";
sendAndWaitForResponse(cmd, null);
}
/** 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.
* <b>signal</b> is one of the following Strings:
* <ul>
* <li>"RELOAD" or "HUP" : Reload config items, refetch directory</li>
* <li>"SHUTDOWN" or "INT" : Controlled shutdown: if server is an OP, exit immediately.
* If it's an OR, close listeners and exit after 30 seconds</li>
* <li>"DUMP" or "USR1" : Dump stats: log information about open connections and circuits</li>
* <li>"DEBUG" or "USR2" : Debug: switch all open logs to loglevel debug</li>
* <li>"HALT" or "TERM" : Immediate shutdown: clean up and exit now</li>
* </ul>
*/
public void signal(String signal) throws IOException {
String cmd = "SIGNAL " + signal + "\r\n";
sendAndWaitForResponse(cmd, null);
}
/** 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);
synchronized (waiters) {
output.write(s);
output.flush();
}
}
/** 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.
*
* 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
* address itself, and return that address in the reply. The server
* 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.
*
* 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.
*
* 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();
int i = kv.indexOf(' ');
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>();
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));
}
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());
}
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);
return m.get(fromAddr);
}
/** Queries the Tor server for keyed values that are not stored in the torrc
* configuration file. Returns a map of keys to values.
*
* Recognized keys include:
* <ul>
* <li>"version" : The version of the server's software, including the name
* of the software. (example: "Tor 0.0.9.4")</li>
* <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest server
* descriptor for a given OR, NUL-terminated. If no such OR is known, the
* corresponding value is an empty string.</li>
* <li>"network-status" : a space-separated list of all known OR identities.
* This is in the same format as the router-status line in directories;
* see tor-spec.txt for details.</li>
* <li>"addr-mappings/all"</li>
* <li>"addr-mappings/config"</li>
* <li>"addr-mappings/cache"</li>
* <li>"addr-mappings/control" : a space-separated list of address mappings, each
* in the form of "from-address=to-address". The 'config' key
* returns those address mappings set in the configuration; the 'cache'
* key returns the mappings in the client-side DNS cache; the 'control'
* key returns the mappings set via the control interface; the 'all'
* target returns the mappings set through any mechanism.</li>
* <li>"circuit-status" : A series of lines as for a circuit status event. Each line is of the form:
* "CircuitID CircStatus Path"</li>
* <li>"stream-status" : A series of lines as for a stream status event. Each is of the form:
* "StreamID StreamStatus CircID Target"</li>
* <li>"orconn-status" : A series of lines as for an OR connection status event. Each is of the
* form: "ServerID ORStatus"</li>
* </ul>
*/
public Map<String,String> getInfo(Collection<String> keys) throws IOException {
StringBuffer sb = new StringBuffer("GETINFO");
for (Iterator<String> it = keys.iterator(); it.hasNext(); ) {
sb.append(" ").append(it.next());
}
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();
int idx = line.msg.indexOf('=');
if (idx<0)
break;
String k = line.msg.substring(0,idx);
String v;
if (line.rest != null) {
v = line.rest;
} else {
v = line.msg.substring(idx+1);
}
m.put(k, v);
}
return m;
}
/** Return 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);
return m.get(key);
}
/** 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>.
*
* 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>.
*
* 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.
*
* 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 {
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.
*/
// 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);
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.
*
* 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",
null);
}
/** 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>
* <li>2 -- REASON_RESOLVEFAILED (couldn't look up hostname)</li>
* <li>3 -- REASON_CONNECTREFUSED (remote host refused connection)</li>
* <li>4 -- REASON_EXITPOLICY (OR refuses to connect to host or port)</li>
* <li>5 -- REASON_DESTROY (Circuit is being destroyed)</li>
* <li>6 -- REASON_DONE (Anonymized TCP connection was closed)</li>
* <li>7 -- REASON_TIMEOUT (Connection timed out, or OR timed out while connecting)</li>
* <li>8 -- (unallocated)</li>
* <li>9 -- REASON_HIBERNATING (OR is temporarily hibernating)</li>
* <li>10 -- REASON_INTERNAL (Internal error at the OR)</li>
* <li>11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill request)</li>
* <li>12 -- REASON_CONNRESET (Connection was unexpectedly reset)</li>
* <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of Tor protocol violations)</li>
* </ul>
*
* 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);
}
/** 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);
}
}

View File

@ -1,39 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.io.IOException;
/**
* An exception raised when Tor tells us about an error.
*/
public class TorControlError extends IOException {
static final long serialVersionUID = 3;
private final 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)
return null;
return TorControlCommands.ERROR_MSGS[errorType];
} catch (ArrayIndexOutOfBoundsException ex) {
return "Unrecongized error #"+errorType;
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.io.IOException;
/**
* An exception raised when Tor behaves in an unexpected way.
*/
public class TorControlSyntaxError extends IOException {
static final long serialVersionUID = 3;
public TorControlSyntaxError(String s) { super(s); }
}

View File

@ -1,44 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control.examples;
import java.io.PrintWriter;
import java.util.Iterator;
import org.torproject.android.control.EventHandler;
public class DebuggingEventHandler implements EventHandler {
private final PrintWriter out;
public DebuggingEventHandler(PrintWriter p) {
out = p;
}
public void circuitStatus(String status, String circID, String path) {
out.println("Circuit "+circID+" is now "+status+" (path="+path+")");
}
public void streamStatus(String status, String streamID, String target) {
out.println("Stream "+streamID+" is now "+status+" (target="+target+")");
}
public void orConnStatus(String status, String orName) {
out.println("OR connection to "+orName+" is now "+status);
}
public void bandwidthUsed(long read, long written) {
out.println("Bandwidth usage: "+read+" bytes read; "+
written+" bytes written.");
}
public void newDescriptors(java.util.List<String> orList) {
out.println("New descriptors for routers:");
for (Iterator<String> i = orList.iterator(); i.hasNext(); )
out.println(" "+i.next());
}
public void message(String type, String msg) {
out.println("["+type+"] "+msg.trim());
}
public void unrecognized(String type, String msg) {
out.println("unrecognized event ["+type+"] "+msg.trim());
}
}

View File

@ -1,146 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control.examples;
import org.torproject.android.control.*;
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.Map;
import java.util.Iterator;
public class Main implements TorControlCommands {
public static void main(String args[]) {
if (args.length < 1) {
System.err.println("No command given.");
return;
}
try {
if (args[0].equals("set-config")) {
setConfig(args);
} else if (args[0].equals("get-config")) {
getConfig(args);
} else if (args[0].equals("get-info")) {
getInfo(args);
} else if (args[0].equals("listen")) {
listenForEvents(args);
} else if (args[0].equals("signal")) {
signal(args);
} else if (args[0].equals("auth")) {
authDemo(args);
} else {
System.err.println("Unrecognized command: "+args[0]);
}
} 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);
}
}
private static TorControlConnection getConnection(String[] args,
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;
}
private static TorControlConnection getConnection(String[] args)
throws IOException {
return getConnection(args, true);
}
public static void setConfig(String[] args) throws IOException {
// Usage: "set-config [-save] key value key value key value"
TorControlConnection conn = getConnection(args);
ArrayList<String> lst = new ArrayList<String>();
int i = 1;
boolean save = false;
if (args[i].equals("-save")) {
save = true;
++i;
}
for (; i < args.length; i +=2) {
lst.add(args[i]+" "+args[i+1]);
}
conn.setConf(lst);
if (save) {
conn.saveConf();
}
}
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();
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();
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]);
}
conn.setEventHandler(
new DebuggingEventHandler(new PrintWriter(System.out, true)));
conn.setEvents(lst);
}
public static void signal(String[] args) throws IOException {
// Usage signal [reload|shutdown|dump|debug|halt]
TorControlConnection conn = getConnection(args, false);
// distinguish shutdown signal from other signals
if ("SHUTDOWN".equalsIgnoreCase(args[1])
|| "HALT".equalsIgnoreCase(args[1])) {
conn.shutdownTor(args[1].toUpperCase());
} else {
conn.signal(args[1].toUpperCase());
}
}
public static void authDemo(String[] args) throws IOException {
PasswordDigest pwd = PasswordDigest.generateDigest();
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());
s = new Socket("127.0.0.1", 9100);
conn = new TorControlConnection(s);
conn.launchThread(true);
conn.authenticate(pwd.getSecret());
}
}

View File

@ -21,7 +21,6 @@ import android.content.Context;
import android.os.Build;
import android.util.Log;
import org.torproject.android.service.R;
public class TorResourceInstaller implements TorServiceConstants {
@ -67,7 +66,7 @@ public class TorResourceInstaller implements TorServiceConstants {
File outFile;
String cpuPath = "armeabi";
if (Build.CPU_ABI.contains("x86"))
cpuPath = "x86";
@ -75,19 +74,14 @@ public class TorResourceInstaller implements TorServiceConstants {
installFolder.mkdirs();
is = context.getResources().openRawResource(R.raw.torrc);
is = context.getAssets().open(COMMON_ASSET_KEY + TORRC_ASSET_KEY);
outFile = new File(installFolder, TORRC_ASSET_KEY);
streamToFile(is,outFile, false, false);
is = context.getResources().openRawResource(R.raw.torpolipo);
is = context.getAssets().open(COMMON_ASSET_KEY + POLIPOCONFIG_ASSET_KEY);
outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY);
streamToFile(is,outFile, false, false);
is = context.getAssets().open(cpuPath + '/' + OBFSCLIENT_ASSET_KEY + MP3_EXT);
outFile = new File(installFolder, OBFSCLIENT_ASSET_KEY);
streamToFile(is,outFile, false, true);
setExecutable(outFile);
is = context.getAssets().open(cpuPath + '/' + TOR_ASSET_KEY + MP3_EXT);
outFile = new File(installFolder, TOR_ASSET_KEY);
streamToFile(is,outFile, false, true);
@ -97,12 +91,7 @@ public class TorResourceInstaller implements TorServiceConstants {
outFile = new File(installFolder, POLIPO_ASSET_KEY);
streamToFile(is,outFile, false, true);
setExecutable(outFile);
is = context.getAssets().open(cpuPath + '/' + PDNSD_ASSET_KEY + MP3_EXT);
outFile = new File(installFolder, PDNSD_ASSET_KEY);
streamToFile(is,outFile, false, true);
setExecutable(outFile);
installGeoIP();
return true;
@ -130,9 +119,8 @@ public class TorResourceInstaller implements TorServiceConstants {
{
InputStream is;
is = context.getResources().openRawResource(R.raw.torpolipo);
is = context.getAssets().open(COMMON_ASSET_KEY + POLIPOCONFIG_ASSET_KEY);
streamToFile(is,filePolipo, false, false);
if (extraLines != null && extraLines.length() > 0)
@ -150,9 +138,8 @@ public class TorResourceInstaller implements TorServiceConstants {
InputStream is;
File outFile;
is = context.getResources().openRawResource(R.raw.torpolipo);
is = context.getAssets().open(COMMON_ASSET_KEY + POLIPOCONFIG_ASSET_KEY);
outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY);
streamToFile(is,outFile, false, false);
@ -170,32 +157,17 @@ public class TorResourceInstaller implements TorServiceConstants {
File outFile;
outFile = new File(installFolder, GEOIP_ASSET_KEY);
is = context.getResources().openRawResource(R.raw.geoip);
is = context.getAssets().open(COMMON_ASSET_KEY + GEOIP_ASSET_KEY + MP3_EXT);
streamToFile(is, outFile, false, true);
is = context.getResources().openRawResource(R.raw.geoip6);
is = context.getAssets().open(COMMON_ASSET_KEY + GEOIP6_ASSET_KEY + MP3_EXT);
outFile = new File(installFolder, GEOIP6_ASSET_KEY);
streamToFile(is, outFile, false, true);
return true;
}
/*
private static void copyAssetFile(Context ctx, String asset, File file) throws IOException, InterruptedException
{
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
InputStream is = new GZIPInputStream(ctx.getAssets().open(asset));
byte buf[] = new byte[8172];
int len;
while ((len = is.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
is.close();
}*/
/*
* Write the inputstream contents to the file
*/
@ -234,81 +206,8 @@ public class TorResourceInstaller implements TorServiceConstants {
return true;
}
//copy the file from inputstream to File output - alternative impl
public static boolean copyFile (InputStream is, File outputFile)
{
try {
if (outputFile.exists())
outputFile.delete();
boolean newFile = outputFile.createNewFile();
DataOutputStream out = new DataOutputStream(new FileOutputStream(outputFile));
DataInputStream in = new DataInputStream(is);
int b = -1;
byte[] data = new byte[1024];
while ((b = in.read(data)) != -1) {
out.write(data);
}
if (b == -1); //rejoice
//
out.flush();
out.close();
in.close();
// chmod?
return newFile;
} catch (IOException ex) {
Log.e(TAG, "error copying binary", ex);
return false;
}
}
/**
* Copies a raw resource file, given its ID to the given location
* @param ctx context
* @param resid resource id
* @param file destination file
* @param mode file permissions (E.g.: "755")
* @throws IOException on error
* @throws InterruptedException when interrupted
*/
public static void copyRawFile(Context ctx, int resid, File file, String mode, boolean isZipd) throws IOException, InterruptedException
{
final String abspath = file.getAbsolutePath();
// Write the iptables binary
final FileOutputStream out = new FileOutputStream(file);
InputStream is = ctx.getResources().openRawResource(resid);
if (isZipd)
{
ZipInputStream zis = new ZipInputStream(is);
ZipEntry ze = zis.getNextEntry();
is = zis;
}
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
is.close();
// Change the permissions
Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor();
}
private void setExecutable(File fileBin) {
fileBin.setReadable(true);

View File

@ -3,24 +3,17 @@
package org.torproject.android.binary;
import android.content.Intent;
public interface TorServiceConstants {
String TAG = "TorBinary";
String DIRECTORY_TOR_BINARY = "bin";
String DIRECTORY_TOR_DATA = "data";
//name of the tor C binary
String TOR_ASSET_KEY = "tor";
//torrc (tor config file)
String TORRC_ASSET_KEY = "torrc";
String TORRCDIAG_ASSET_KEY = "torrcdiag";
String TORRC_TETHER_KEY = "torrctether";
String TOR_CONTROL_COOKIE = "control_auth_cookie";
String COMMON_ASSET_KEY = "common/";
//privoxy
String POLIPO_ASSET_KEY = "polipo";
@ -31,17 +24,8 @@ public interface TorServiceConstants {
String GEOIP_ASSET_KEY = "geoip";
String GEOIP6_ASSET_KEY = "geoip6";
String SHELL_CMD_PS = "toolbox ps";
int FILE_WRITE_BUFFER_SIZE = 1024;
//obfsproxy
String OBFSCLIENT_ASSET_KEY = "obfs4proxy";
//DNS daemon for TCP DNS over TOr
String PDNSD_ASSET_KEY = "pdnsd";
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
obfs2=obfsclient
obfs3=obfsclient
scramblesuit=obfsclient

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pdnsd_conf" formatted="true">
global {
perm_cache=0;
cache_dir="/data/data/org.torproject.android/app_bin";
server_port = 8091;
server_ip = 0.0.0.0;
query_method=udp_only;
min_ttl=15m;
max_ttl=1w;
timeout=10;
daemon=on;
pid_file="/data/data/org.torproject.android/app_bin/pdnsd.pid";
}
server {
label= "upstream";
ip = %1$s;
port = %2$d;
uptest = none;
}
rr {
name=localhost;
reverse=on;
a=127.0.0.1;
owner=localhost;
soa=localhost,root.localhost,42,86400,900,86400,86400;
}
</string>
</resources>

View File

@ -1,114 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.util.Arrays;
import java.util.List;
/**
* Static class to do bytewise structure manipulation in Java.
*/
/* XXXX There must be a better way to do most of this.
* XXXX The string logic here uses default encoding, which is stupid.
*/
final class Bytes {
/** Write the two-byte value in 's' into the byte array 'ba', starting at
* the index 'pos'. */
public static void setU16(byte[] ba, int pos, short s) {
ba[pos] = (byte)((s >> 8) & 0xff);
ba[pos+1] = (byte)((s ) & 0xff);
}
/** Write the four-byte value in 'i' into the byte array 'ba', starting at
* the index 'pos'. */
public static void setU32(byte[] ba, int pos, int i) {
ba[pos] = (byte)((i >> 24) & 0xff);
ba[pos+1] = (byte)((i >> 16) & 0xff);
ba[pos+2] = (byte)((i >> 8) & 0xff);
ba[pos+3] = (byte)((i ) & 0xff);
}
/** Return the four-byte value starting at index 'pos' within 'ba' */
public static int getU32(byte[] ba, int pos) {
return
((ba[pos ]&0xff)<<24) |
((ba[pos+1]&0xff)<<16) |
((ba[pos+2]&0xff)<< 8) |
((ba[pos+3]&0xff));
}
public static String getU32S(byte[] ba, int pos) {
return String.valueOf( (getU32(ba,pos))&0xffffffffL );
}
/** Return the two-byte value starting at index 'pos' within 'ba' */
public static int getU16(byte[] ba, int pos) {
return
((ba[pos ]&0xff)<<8) |
((ba[pos+1]&0xff));
}
/** Return the string starting at position 'pos' of ba and extending
* until a zero byte or the end of the string. */
public static String getNulTerminatedStr(byte[] ba, int pos) {
int len, maxlen = ba.length-pos;
for (len=0; len<maxlen; ++len) {
if (ba[pos+len] == 0)
break;
}
return new String(ba, pos, len);
}
/**
* Read bytes from 'ba' starting at 'pos', dividing them into strings
* along the character in 'split' and writing them into 'lst'
*/
public static void splitStr(List<String> lst, byte[] ba, int pos, byte split) {
while (pos < ba.length && ba[pos] != 0) {
int len;
for (len=0; pos+len < ba.length; ++len) {
if (ba[pos+len] == 0 || ba[pos+len] == split)
break;
}
if (len>0)
lst.add(new String(ba, pos, len));
pos += len;
if (ba[pos] == split)
++pos;
}
}
/**
* Read bytes from 'ba' starting at 'pos', dividing them into strings
* along the character in 'split' and writing them into 'lst'
*/
public static List<String> splitStr(List<String> lst, String str) {
// split string on spaces, include trailing/leading
String[] tokenArray = str.split(" ", -1);
if (lst == null) {
lst = Arrays.asList( tokenArray );
} else {
lst.addAll( Arrays.asList( tokenArray ) );
}
return lst;
}
private static final char[] NYBBLES = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
public static final String hex(byte[] ba) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < ba.length; ++i) {
int b = (ba[i]) & 0xff;
buf.append(NYBBLES[b >> 4]);
buf.append(NYBBLES[b&0x0f]);
}
return buf.toString();
}
private Bytes() {};
}

View File

@ -1,20 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/** A single key-value pair from Tor's configuration. */
public class ConfigEntry {
public ConfigEntry(String k, String v) {
key = k;
value = v;
is_default = false;
}
public ConfigEntry(String k) {
key = k;
value = "";
is_default = true;
}
public final String key;
public final String value;
public final boolean is_default;
}

View File

@ -1,75 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/**
* Abstract interface whose methods are invoked when Tor sends us an event.
*
* @see TorControlConnection#setEventHandler
* @see TorControlConnection#setEvents
*/
public interface EventHandler {
/**
* Invoked when a circuit's status has changed.
* Possible values for <b>status</b> are:
* <ul>
* <li>"LAUNCHED" : circuit ID assigned to new circuit</li>
* <li>"BUILT" : all hops finished, can now accept streams</li>
* <li>"EXTENDED" : one more hop has been completed</li>
* <li>"FAILED" : circuit closed (was not built)</li>
* <li>"CLOSED" : circuit closed (was built)</li>
* </ul>
*
* <b>circID</b> is the alphanumeric identifier of the affected circuit,
* and <b>path</b> is a comma-separated list of alphanumeric ServerIDs.
*/
public void circuitStatus(String status, String circID, String path);
/**
* Invoked when a stream's status has changed.
* Possible values for <b>status</b> are:
* <ul>
* <li>"NEW" : New request to connect</li>
* <li>"NEWRESOLVE" : New request to resolve an address</li>
* <li>"SENTCONNECT" : Sent a connect cell along a circuit</li>
* <li>"SENTRESOLVE" : Sent a resolve cell along a circuit</li>
* <li>"SUCCEEDED" : Received a reply; stream established</li>
* <li>"FAILED" : Stream failed and not retriable.</li>
* <li>"CLOSED" : Stream closed</li>
* <li>"DETACHED" : Detached from circuit; still retriable.</li>
* </ul>
*
* <b>streamID</b> is the alphanumeric identifier of the affected stream,
* and its <b>target</b> is specified as address:port.
*/
public void streamStatus(String status, String streamID, String target);
/**
* Invoked when the status of a connection to an OR has changed.
* Possible values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | "CLOSED"].
* <b>orName</b> is the alphanumeric identifier of the OR affected.
*/
public void orConnStatus(String status, String orName);
/**
* Invoked once per second. <b>read</b> and <b>written</b> are
* the number of bytes read and written, respectively, in
* the last second.
*/
public void bandwidthUsed(long read, long written);
/**
* Invoked whenever Tor learns about new ORs. The <b>orList</b> object
* contains the alphanumeric ServerIDs associated with the new ORs.
*/
public void newDescriptors(java.util.List<String> orList);
/**
* Invoked when Tor logs a message.
* <b>severity</b> is one of ["DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERR"],
* and <b>msg</b> is the message string.
*/
public void message(String severity, String msg);
/**
* Invoked when an unspecified message is received.
* <type> is the message type, and <msg> is the message string.
*/
public void unrecognized(String type, String msg);
}

View File

@ -1,18 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/**
* Implementation of EventHandler that ignores all events. Useful
* when you only want to override one method.
*/
public class NullEventHandler implements EventHandler {
public void circuitStatus(String status, String circID, String path) {}
public void streamStatus(String status, String streamID, String target) {}
public void orConnStatus(String status, String orName) {}
public void bandwidthUsed(long read, long written) {}
public void newDescriptors(java.util.List<String> orList) {}
public void message(String severity, String msg) {}
public void unrecognized(String type, String msg) {}
}

View File

@ -1,98 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* A hashed digest of a secret password (used to set control connection
* security.)
*
* For the actual hashing algorithm, see RFC2440's secret-to-key conversion.
*/
public class PasswordDigest {
private final byte[] secret;
private final String hashedKey;
/** Return a new password digest with a random secret and salt. */
public static PasswordDigest generateDigest() {
byte[] secret = new byte[20];
SecureRandom rng = new SecureRandom();
rng.nextBytes(secret);
return new PasswordDigest(secret);
}
/** Construct a new password digest with a given secret and random salt */
public PasswordDigest(byte[] secret) {
this(secret, null);
}
/** Construct a new password digest with a given secret and random salt.
* Note that the 9th byte of the specifier determines the number of hash
* iterations as in RFC2440.
*/
public PasswordDigest(byte[] secret, byte[] specifier) {
this.secret = secret.clone();
if (specifier == null) {
specifier = new byte[9];
SecureRandom rng = new SecureRandom();
rng.nextBytes(specifier);
specifier[8] = 96;
}
hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier));
}
/** Return the secret used to generate this password hash.
*/
public byte[] getSecret() {
return secret.clone();
}
/** Return the hashed password in the format used by Tor. */
public String getHashedPassword() {
return hashedKey;
}
/** Parameter used by RFC2440's s2k algorithm. */
private static final int EXPBIAS = 6;
/** Implement rfc2440 s2k */
public static byte[] secretToKey(byte[] secret, byte[] specifier) {
MessageDigest d;
try {
d = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("Can't run without sha-1.");
}
int c = (specifier[8])&0xff;
int count = (16 + (c&15)) << ((c>>4) + EXPBIAS);
byte[] tmp = new byte[8+secret.length];
System.arraycopy(specifier, 0, tmp, 0, 8);
System.arraycopy(secret, 0, tmp, 8, secret.length);
while (count > 0) {
if (count >= tmp.length) {
d.update(tmp);
count -= tmp.length;
} else {
d.update(tmp, 0, count);
count = 0;
}
}
byte[] key = new byte[20+9];
System.arraycopy(d.digest(), 0, key, 9, 20);
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);
}
}

View File

@ -1,4 +0,0 @@
We broke the version detection stuff in Tor 0.1.2.16 / 0.2.0.4-alpha.
Somebody should rip out the v0 control protocol stuff from here, and
it should start working again. -RD

View File

@ -1,148 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
/** Interface defining constants used by the Tor controller protocol.
*/
// XXXX Take documentation for these from control-spec.txt
public interface TorControlCommands {
public static final short CMD_ERROR = 0x0000;
public static final short CMD_DONE = 0x0001;
public static final short CMD_SETCONF = 0x0002;
public static final short CMD_GETCONF = 0x0003;
public static final short CMD_CONFVALUE = 0x0004;
public static final short CMD_SETEVENTS = 0x0005;
public static final short CMD_EVENT = 0x0006;
public static final short CMD_AUTH = 0x0007;
public static final short CMD_SAVECONF = 0x0008;
public static final short CMD_SIGNAL = 0x0009;
public static final short CMD_MAPADDRESS = 0x000A;
public static final short CMD_GETINFO = 0x000B;
public static final short CMD_INFOVALUE = 0x000C;
public static final short CMD_EXTENDCIRCUIT = 0x000D;
public static final short CMD_ATTACHSTREAM = 0x000E;
public static final short CMD_POSTDESCRIPTOR = 0x000F;
public static final short CMD_FRAGMENTHEADER = 0x0010;
public static final short CMD_FRAGMENT = 0x0011;
public static final short CMD_REDIRECTSTREAM = 0x0012;
public static final short CMD_CLOSESTREAM = 0x0013;
public static final short CMD_CLOSECIRCUIT = 0x0014;
public static final String[] CMD_NAMES = {
"ERROR",
"DONE",
"SETCONF",
"GETCONF",
"CONFVALUE",
"SETEVENTS",
"EVENT",
"AUTH",
"SAVECONF",
"SIGNAL",
"MAPADDRESS",
"GETINFO",
"INFOVALUE",
"EXTENDCIRCUIT",
"ATTACHSTREAM",
"POSTDESCRIPTOR",
"FRAGMENTHEADER",
"FRAGMENT",
"REDIRECTSTREAM",
"CLOSESTREAM",
"CLOSECIRCUIT",
};
public static final short EVENT_CIRCSTATUS = 0x0001;
public static final short EVENT_STREAMSTATUS = 0x0002;
public static final short EVENT_ORCONNSTATUS = 0x0003;
public static final short EVENT_BANDWIDTH = 0x0004;
public static final short EVENT_NEWDESCRIPTOR = 0x0006;
public static final short EVENT_MSG_DEBUG = 0x0007;
public static final short EVENT_MSG_INFO = 0x0008;
public static final short EVENT_MSG_NOTICE = 0x0009;
public static final short EVENT_MSG_WARN = 0x000A;
public static final short EVENT_MSG_ERROR = 0x000B;
public static final String[] EVENT_NAMES = {
"(0)",
"CIRC",
"STREAM",
"ORCONN",
"BW",
"OLDLOG",
"NEWDESC",
"DEBUG",
"INFO",
"NOTICE",
"WARN",
"ERR",
};
public static final byte CIRC_STATUS_LAUNCHED = 0x01;
public static final byte CIRC_STATUS_BUILT = 0x02;
public static final byte CIRC_STATUS_EXTENDED = 0x03;
public static final byte CIRC_STATUS_FAILED = 0x04;
public static final byte CIRC_STATUS_CLOSED = 0x05;
public static final String[] CIRC_STATUS_NAMES = {
"LAUNCHED",
"BUILT",
"EXTENDED",
"FAILED",
"CLOSED",
};
public static final byte STREAM_STATUS_SENT_CONNECT = 0x00;
public static final byte STREAM_STATUS_SENT_RESOLVE = 0x01;
public static final byte STREAM_STATUS_SUCCEEDED = 0x02;
public static final byte STREAM_STATUS_FAILED = 0x03;
public static final byte STREAM_STATUS_CLOSED = 0x04;
public static final byte STREAM_STATUS_NEW_CONNECT = 0x05;
public static final byte STREAM_STATUS_NEW_RESOLVE = 0x06;
public static final byte STREAM_STATUS_DETACHED = 0x07;
public static final String[] STREAM_STATUS_NAMES = {
"SENT_CONNECT",
"SENT_RESOLVE",
"SUCCEEDED",
"FAILED",
"CLOSED",
"NEW_CONNECT",
"NEW_RESOLVE",
"DETACHED"
};
public static final byte OR_CONN_STATUS_LAUNCHED = 0x00;
public static final byte OR_CONN_STATUS_CONNECTED = 0x01;
public static final byte OR_CONN_STATUS_FAILED = 0x02;
public static final byte OR_CONN_STATUS_CLOSED = 0x03;
public static final String[] OR_CONN_STATUS_NAMES = {
"LAUNCHED","CONNECTED","FAILED","CLOSED"
};
public static final byte SIGNAL_HUP = 0x01;
public static final byte SIGNAL_INT = 0x02;
public static final byte SIGNAL_USR1 = 0x0A;
public static final byte SIGNAL_USR2 = 0x0C;
public static final byte SIGNAL_TERM = 0x0F;
public static final String ERROR_MSGS[] = {
"Unspecified error",
"Internal error",
"Unrecognized message type",
"Syntax error",
"Unrecognized configuration key",
"Invalid configuration value",
"Unrecognized byte code",
"Unauthorized",
"Failed authentication attempt",
"Resource exhausted",
"No such stream",
"No such circuit",
"No such OR",
};
}

View File

@ -1,730 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.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.util.ArrayList;
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;
/** A connection to a running Tor process as specified in control-spec.txt. */
public class TorControlConnection implements TorControlCommands {
private final LinkedList<Waiter> waiters;
private final BufferedReader input;
private final Writer output;
private ControlParseThread thread; // Locking: this
private volatile EventHandler handler;
private volatile PrintWriter debugOutput;
private volatile IOException parseThreadException;
static class Waiter {
List<ReplyLine> response; // Locking: this
synchronized List<ReplyLine> getResponse() throws InterruptedException {
while (response == null) {
wait();
}
return response;
}
synchronized void setResponse(List<ReplyLine> response) {
this.response = response;
notifyAll();
}
}
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;
}
}
/** 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(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(InputStream i, OutputStream o) {
this(new InputStreamReader(i), new OutputStreamWriter(o));
}
public TorControlConnection(Reader i, Writer o) {
this.output = o;
if (i instanceof BufferedReader)
this.input = (BufferedReader) i;
else
this.input = new BufferedReader(i);
this.waiters = new LinkedList<Waiter>();
}
protected final void writeEscaped(String s) throws IOException {
StringTokenizer st = new StringTokenizer(s, "\n");
while (st.hasMoreTokens()) {
String line = st.nextToken();
if (line.startsWith("."))
line = "."+line;
if (line.endsWith("\r"))
line += "\n";
else
line += "\r\n";
if (debugOutput != null)
debugOutput.print(">> "+line);
output.write(line);
}
output.write(".\r\n");
if (debugOutput != null)
debugOutput.print(">> .\n");
}
protected static final String quote(String s) {
StringBuffer sb = new StringBuffer("\"");
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
switch (c)
{
case '\r':
case '\n':
case '\\':
case '\"':
sb.append('\\');
}
sb.append(c);
}
sb.append('\"');
return sb.toString();
}
protected final ArrayList<ReplyLine> readReply() throws IOException {
ArrayList<ReplyLine> reply = new ArrayList<ReplyLine>();
char c;
do {
String line = input.readLine();
if (line == null) {
// if line is null, the end of the stream has been reached, i.e.
// the connection to Tor has been closed!
if (reply.isEmpty()) {
// nothing received so far, can exit cleanly
return reply;
}
// received half of a reply before the connection broke down
throw new TorControlSyntaxError("Connection to Tor " +
" broke down while receiving reply!");
}
if (debugOutput != null)
debugOutput.println("<< "+line);
if (line.length() < 4)
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;
if (c == '+') {
StringBuffer data = new StringBuffer();
while (true) {
line = input.readLine();
if (debugOutput != null)
debugOutput.print("<< "+line);
if (line.equals("."))
break;
else if (line.startsWith("."))
line = line.substring(1);
data.append(line).append('\n');
}
rest = data.toString();
}
reply.add(new ReplyLine(status, msg, rest));
} while (c != ' ');
return reply;
}
protected synchronized List<ReplyLine> sendAndWaitForResponse(String s,
String rest) throws IOException {
if(parseThreadException != null) throw parseThreadException;
checkThread();
Waiter w = new Waiter();
if (debugOutput != null)
debugOutput.print(">> "+s);
synchronized (waiters) {
output.write(s);
if (rest != null)
writeEscaped(rest);
output.flush();
waiters.addLast(w);
}
List<ReplyLine> lst;
try {
lst = w.getResponse();
} 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);
}
return lst;
}
/** Helper: decode a CMD_EVENT command and dispatch it to our
* EventHandler (if any). */
protected void handleEvent(ArrayList<ReplyLine> events) {
if (handler == null)
return;
for (Iterator<ReplyLine> i = events.iterator(); i.hasNext(); ) {
ReplyLine line = i.next();
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() < 3 ? ""
: lst.get(2));
} else if (tp.equals("STREAM")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.streamStatus(lst.get(1),
lst.get(0),
lst.get(3));
// XXXX circID.
} else if (tp.equals("ORCONN")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.orConnStatus(lst.get(1), lst.get(0));
} else if (tp.equals("BW")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.bandwidthUsed(Integer.parseInt(lst.get(0)),
Integer.parseInt(lst.get(1)));
} else if (tp.equals("NEWDESC")) {
List<String> lst = Bytes.splitStr(null, rest);
handler.newDescriptors(lst);
} else if (tp.equals("DEBUG") ||
tp.equals("INFO") ||
tp.equals("NOTICE") ||
tp.equals("WARN") ||
tp.equals("ERR")) {
handler.message(tp, rest);
} else {
handler.unrecognized(tp, rest);
}
}
}
/** 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 "\<\<"
*/
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 "\<\<"
*/
public void setDebugging(PrintStream s) {
debugOutput = new PrintWriter(s, true);
}
/** Set the EventHandler object that will be notified of any
* events Tor delivers to this connection. To make Tor send us
* events, call setEvents(). */
public void setEventHandler(EventHandler handler) {
this.handler = handler;
}
/**
* Start a thread to react to Tor's responses in the background.
* This is necessary to handle asynchronous events and synchronous
* responses that arrive independantly over the same socket.
*/
public synchronized Thread launchThread(boolean daemon) {
ControlParseThread th = new ControlParseThread();
if (daemon)
th.setDaemon(true);
th.start();
this.thread = th;
return th;
}
protected class ControlParseThread extends Thread {
@Override
public void run() {
try {
react();
} catch (IOException ex) {
parseThreadException = ex;
}
}
}
protected synchronized void checkThread() {
if (thread == null)
launchThread(true);
}
/** helper: implement the main background loop. */
protected void react() throws IOException {
while (true) {
ArrayList<ReplyLine> lst = readReply();
if (lst.isEmpty()) {
// connection has been closed remotely! end the loop!
return;
}
if ((lst.get(0)).status.startsWith("6"))
handleEvent(lst);
else {
synchronized (waiters) {
if (!waiters.isEmpty())
{
Waiter w;
w = waiters.removeFirst();
w.setResponse(lst);
}
}
}
}
}
/** 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);
}
/** 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");
}
setConf(lst);
}
/** 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();
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("\r\n");
sendAndWaitForResponse(b.toString(), null);
}
/** Try to reset the values listed in the collection 'keys' to their
* default values.
**/
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();
b.append(" ").append(key);
}
b.append("\r\n");
sendAndWaitForResponse(b.toString(), null);
}
/** Return the value of the configuration option 'key' */
public List<ConfigEntry> getConf(String key) throws IOException {
List<String> lst = new ArrayList<String>();
lst.add(key);
return getConf(lst);
}
/** 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"
* virtual keyword to get all HiddenServiceDir, HiddenServicePort,
* 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();
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;
int idx = kv.indexOf('=');
if (idx >= 0)
result.add(new ConfigEntry(kv.substring(0, idx),
kv.substring(idx+1)));
else
result.add(new ConfigEntry(kv));
}
return result;
}
/** Request that the server inform the client about interesting events.
* Each element of <b>events</b> is one of the following Strings:
* ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" |
* "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] .
*
* 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());
}
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]).
*
* 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>.
*
* 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>'
* 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.
*/
public void authenticate(byte[] auth) throws IOException {
String cmd = "AUTHENTICATE " + Bytes.hex(auth) + "\r\n";
sendAndWaitForResponse(cmd, null);
}
/** 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.
* <b>signal</b> is one of the following Strings:
* <ul>
* <li>"RELOAD" or "HUP" : Reload config items, refetch directory</li>
* <li>"SHUTDOWN" or "INT" : Controlled shutdown: if server is an OP, exit immediately.
* If it's an OR, close listeners and exit after 30 seconds</li>
* <li>"DUMP" or "USR1" : Dump stats: log information about open connections and circuits</li>
* <li>"DEBUG" or "USR2" : Debug: switch all open logs to loglevel debug</li>
* <li>"HALT" or "TERM" : Immediate shutdown: clean up and exit now</li>
* </ul>
*/
public void signal(String signal) throws IOException {
String cmd = "SIGNAL " + signal + "\r\n";
sendAndWaitForResponse(cmd, null);
}
/** 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);
synchronized (waiters) {
output.write(s);
output.flush();
}
}
/** 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.
*
* 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
* address itself, and return that address in the reply. The server
* 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.
*
* 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.
*
* 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();
int i = kv.indexOf(' ');
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>();
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));
}
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());
}
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);
return m.get(fromAddr);
}
/** Queries the Tor server for keyed values that are not stored in the torrc
* configuration file. Returns a map of keys to values.
*
* Recognized keys include:
* <ul>
* <li>"version" : The version of the server's software, including the name
* of the software. (example: "Tor 0.0.9.4")</li>
* <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest server
* descriptor for a given OR, NUL-terminated. If no such OR is known, the
* corresponding value is an empty string.</li>
* <li>"network-status" : a space-separated list of all known OR identities.
* This is in the same format as the router-status line in directories;
* see tor-spec.txt for details.</li>
* <li>"addr-mappings/all"</li>
* <li>"addr-mappings/config"</li>
* <li>"addr-mappings/cache"</li>
* <li>"addr-mappings/control" : a space-separated list of address mappings, each
* in the form of "from-address=to-address". The 'config' key
* returns those address mappings set in the configuration; the 'cache'
* key returns the mappings in the client-side DNS cache; the 'control'
* key returns the mappings set via the control interface; the 'all'
* target returns the mappings set through any mechanism.</li>
* <li>"circuit-status" : A series of lines as for a circuit status event. Each line is of the form:
* "CircuitID CircStatus Path"</li>
* <li>"stream-status" : A series of lines as for a stream status event. Each is of the form:
* "StreamID StreamStatus CircID Target"</li>
* <li>"orconn-status" : A series of lines as for an OR connection status event. Each is of the
* form: "ServerID ORStatus"</li>
* </ul>
*/
public Map<String,String> getInfo(Collection<String> keys) throws IOException {
StringBuffer sb = new StringBuffer("GETINFO");
for (Iterator<String> it = keys.iterator(); it.hasNext(); ) {
sb.append(" ").append(it.next());
}
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();
int idx = line.msg.indexOf('=');
if (idx<0)
break;
String k = line.msg.substring(0,idx);
String v;
if (line.rest != null) {
v = line.rest;
} else {
v = line.msg.substring(idx+1);
}
m.put(k, v);
}
return m;
}
/** Return 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);
return m.get(key);
}
/** 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>.
*
* 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>.
*
* 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.
*
* 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 {
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.
*/
// 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);
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.
*
* 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",
null);
}
/** 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>
* <li>2 -- REASON_RESOLVEFAILED (couldn't look up hostname)</li>
* <li>3 -- REASON_CONNECTREFUSED (remote host refused connection)</li>
* <li>4 -- REASON_EXITPOLICY (OR refuses to connect to host or port)</li>
* <li>5 -- REASON_DESTROY (Circuit is being destroyed)</li>
* <li>6 -- REASON_DONE (Anonymized TCP connection was closed)</li>
* <li>7 -- REASON_TIMEOUT (Connection timed out, or OR timed out while connecting)</li>
* <li>8 -- (unallocated)</li>
* <li>9 -- REASON_HIBERNATING (OR is temporarily hibernating)</li>
* <li>10 -- REASON_INTERNAL (Internal error at the OR)</li>
* <li>11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill request)</li>
* <li>12 -- REASON_CONNRESET (Connection was unexpectedly reset)</li>
* <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of Tor protocol violations)</li>
* </ul>
*
* 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);
}
/** 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);
}
}

View File

@ -1,39 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.io.IOException;
/**
* An exception raised when Tor tells us about an error.
*/
public class TorControlError extends IOException {
static final long serialVersionUID = 3;
private final 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)
return null;
return TorControlCommands.ERROR_MSGS[errorType];
} catch (ArrayIndexOutOfBoundsException ex) {
return "Unrecongized error #"+errorType;
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control;
import java.io.IOException;
/**
* An exception raised when Tor behaves in an unexpected way.
*/
public class TorControlSyntaxError extends IOException {
static final long serialVersionUID = 3;
public TorControlSyntaxError(String s) { super(s); }
}

View File

@ -1,44 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control.examples;
import java.io.PrintWriter;
import java.util.Iterator;
import org.torproject.android.control.EventHandler;
public class DebuggingEventHandler implements EventHandler {
private final PrintWriter out;
public DebuggingEventHandler(PrintWriter p) {
out = p;
}
public void circuitStatus(String status, String circID, String path) {
out.println("Circuit "+circID+" is now "+status+" (path="+path+")");
}
public void streamStatus(String status, String streamID, String target) {
out.println("Stream "+streamID+" is now "+status+" (target="+target+")");
}
public void orConnStatus(String status, String orName) {
out.println("OR connection to "+orName+" is now "+status);
}
public void bandwidthUsed(long read, long written) {
out.println("Bandwidth usage: "+read+" bytes read; "+
written+" bytes written.");
}
public void newDescriptors(java.util.List<String> orList) {
out.println("New descriptors for routers:");
for (Iterator<String> i = orList.iterator(); i.hasNext(); )
out.println(" "+i.next());
}
public void message(String type, String msg) {
out.println("["+type+"] "+msg.trim());
}
public void unrecognized(String type, String msg) {
out.println("unrecognized event ["+type+"] "+msg.trim());
}
}

View File

@ -1,146 +0,0 @@
// Copyright 2005 Nick Mathewson, Roger Dingledine
// See LICENSE file for copying information
package org.torproject.android.control.examples;
import org.torproject.android.control.*;
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.Map;
import java.util.Iterator;
public class Main implements TorControlCommands {
public static void main(String args[]) {
if (args.length < 1) {
System.err.println("No command given.");
return;
}
try {
if (args[0].equals("set-config")) {
setConfig(args);
} else if (args[0].equals("get-config")) {
getConfig(args);
} else if (args[0].equals("get-info")) {
getInfo(args);
} else if (args[0].equals("listen")) {
listenForEvents(args);
} else if (args[0].equals("signal")) {
signal(args);
} else if (args[0].equals("auth")) {
authDemo(args);
} else {
System.err.println("Unrecognized command: "+args[0]);
}
} 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);
}
}
private static TorControlConnection getConnection(String[] args,
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;
}
private static TorControlConnection getConnection(String[] args)
throws IOException {
return getConnection(args, true);
}
public static void setConfig(String[] args) throws IOException {
// Usage: "set-config [-save] key value key value key value"
TorControlConnection conn = getConnection(args);
ArrayList<String> lst = new ArrayList<String>();
int i = 1;
boolean save = false;
if (args[i].equals("-save")) {
save = true;
++i;
}
for (; i < args.length; i +=2) {
lst.add(args[i]+" "+args[i+1]);
}
conn.setConf(lst);
if (save) {
conn.saveConf();
}
}
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();
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();
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]);
}
conn.setEventHandler(
new DebuggingEventHandler(new PrintWriter(System.out, true)));
conn.setEvents(lst);
}
public static void signal(String[] args) throws IOException {
// Usage signal [reload|shutdown|dump|debug|halt]
TorControlConnection conn = getConnection(args, false);
// distinguish shutdown signal from other signals
if ("SHUTDOWN".equalsIgnoreCase(args[1])
|| "HALT".equalsIgnoreCase(args[1])) {
conn.shutdownTor(args[1].toUpperCase());
} else {
conn.signal(args[1].toUpperCase());
}
}
public static void authDemo(String[] args) throws IOException {
PasswordDigest pwd = PasswordDigest.generateDigest();
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());
s = new Socket("127.0.0.1", 9100);
conn = new TorControlConnection(s);
conn.launchThread(true);
conn.authenticate(pwd.getSecret());
}
}

View File

@ -1,15 +0,0 @@
package org.torproject.android.service;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}