mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Merge branch '149-add-onion' into 'master'
Use control port to create and set up Tor hidden service Closes #149 See merge request !160
This commit is contained in:
Binary file not shown.
@@ -47,6 +47,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -63,20 +64,21 @@ import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
||||
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
||||
|
||||
class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
static final TransportId ID = new TransportId("tor");
|
||||
|
||||
private static final String PROP_ONION = "onion";
|
||||
private static final String[] EVENTS = {
|
||||
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
|
||||
};
|
||||
private static final String OWNER = "__OwningControllerProcess";
|
||||
private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
|
||||
private static final int COOKIE_TIMEOUT = 3000; // Milliseconds
|
||||
private static final int HOSTNAME_TIMEOUT = 30 * 1000; // Milliseconds
|
||||
private static final Pattern ONION =
|
||||
Pattern.compile("[a-z2-7]{16}\\.onion");
|
||||
private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}");
|
||||
private static final int MIN_DESCRIPTORS_PUBLISHED = 3;
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(TorPlugin.class.getName());
|
||||
@@ -90,8 +92,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
private final String architecture;
|
||||
private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout;
|
||||
private final ConnectionStatus connectionStatus;
|
||||
private final File torDirectory, torFile, geoIpFile, configFile, doneFile;
|
||||
private final File cookieFile, hostnameFile;
|
||||
private final File torDirectory, torFile, geoIpFile, configFile;
|
||||
private final File doneFile, cookieFile;
|
||||
private final PowerManager.WakeLock wakeLock;
|
||||
|
||||
private volatile boolean running = false;
|
||||
@@ -124,7 +126,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
configFile = new File(torDirectory, "torrc");
|
||||
doneFile = new File(torDirectory, "done");
|
||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||
hostnameFile = new File(torDirectory, "hs/hostname");
|
||||
Object o = appContext.getSystemService(POWER_SERVICE);
|
||||
PowerManager pm = (PowerManager) o;
|
||||
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "TorPlugin");
|
||||
@@ -421,47 +422,39 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
private void publishHiddenService(String port) {
|
||||
if (!running) return;
|
||||
if (!hostnameFile.exists()) {
|
||||
LOG.info("Creating hidden service");
|
||||
try {
|
||||
// Watch for the hostname file being created/updated
|
||||
File serviceDirectory = hostnameFile.getParentFile();
|
||||
serviceDirectory.mkdirs();
|
||||
hostnameFile.createNewFile();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
FileObserver obs = new WriteObserver(hostnameFile, latch);
|
||||
obs.startWatching();
|
||||
// Use the control connection to update the Tor config
|
||||
List<String> config = Arrays.asList(
|
||||
"HiddenServiceDir " +
|
||||
serviceDirectory.getAbsolutePath(),
|
||||
"HiddenServicePort 80 127.0.0.1:" + port);
|
||||
controlConnection.setConf(config);
|
||||
controlConnection.saveConf();
|
||||
// Wait for the hostname file to be created/updated
|
||||
if (!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) {
|
||||
LOG.warning("Hidden service not created");
|
||||
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
|
||||
return;
|
||||
}
|
||||
if (!running) return;
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while creating hidden service");
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Publish the hidden service's onion hostname in transport properties
|
||||
LOG.info("Creating hidden service");
|
||||
String privKey = callback.getSettings().get(HS_PRIVKEY);
|
||||
Map<Integer, String> portLines =
|
||||
Collections.singletonMap(80, "127.0.0.1:" + port);
|
||||
Map<String, String> response;
|
||||
try {
|
||||
String hostname = new String(read(hostnameFile), "UTF-8").trim();
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("onion", hostname);
|
||||
callback.mergeLocalProperties(p);
|
||||
// Use the control connection to set up the hidden service
|
||||
if (privKey == null)
|
||||
response = controlConnection.addOnion(portLines);
|
||||
else response = controlConnection.addOnion(privKey, portLines);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return;
|
||||
}
|
||||
if (!response.containsKey(HS_ADDRESS)) {
|
||||
LOG.warning("Tor did not return a hidden service address");
|
||||
return;
|
||||
}
|
||||
if (privKey == null && !response.containsKey(HS_PRIVKEY)) {
|
||||
LOG.warning("Tor did not return a private key");
|
||||
return;
|
||||
}
|
||||
// Publish the hidden service's onion hostname in transport properties
|
||||
String hostname = response.get(HS_ADDRESS);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_ONION, hostname);
|
||||
callback.mergeLocalProperties(p);
|
||||
if (privKey == null) {
|
||||
// Save the hidden service's private key for next time
|
||||
Settings s = new Settings();
|
||||
s.put(HS_PRIVKEY, response.get(HS_PRIVKEY));
|
||||
callback.mergeSettings(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,7 +544,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (!isRunning()) return null;
|
||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
||||
if (p == null) return null;
|
||||
String onion = p.get("onion");
|
||||
String onion = p.get(PROP_ONION);
|
||||
if (StringUtils.isNullOrEmpty(onion)) return null;
|
||||
if (!ONION.matcher(onion).matches()) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
|
||||
@@ -559,10 +552,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
try {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + onion);
|
||||
controlConnection.forgetHiddenService(onion.substring(0, 16));
|
||||
controlConnection.forgetHiddenService(onion);
|
||||
Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT);
|
||||
proxy.resolveAddrLocally(false);
|
||||
Socket s = new SocksSocket(proxy, onion, 80);
|
||||
Socket s = new SocksSocket(proxy, onion + ".onion", 80);
|
||||
s.setSoTimeout(socketTimeout);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Connected to " + onion);
|
||||
return new TorTransportConnection(this, s);
|
||||
|
||||
@@ -1,8 +1,125 @@
|
||||
diff --git a/net/freehaven/tor/control/TorControlCommands.java b/net/freehaven/tor/control/TorControlCommands.java
|
||||
index 36482d5..14486e3 100644
|
||||
--- a/net/freehaven/tor/control/TorControlCommands.java
|
||||
+++ b/net/freehaven/tor/control/TorControlCommands.java
|
||||
@@ -144,5 +144,8 @@ public interface TorControlCommands {
|
||||
"No such OR",
|
||||
};
|
||||
|
||||
+ public static final String HS_ADDRESS = "onionAddress";
|
||||
+ public static final String HS_PRIVKEY = "onionPrivKey";
|
||||
+
|
||||
}
|
||||
|
||||
diff --git a/net/freehaven/tor/control/TorControlConnection.java b/net/freehaven/tor/control/TorControlConnection.java
|
||||
index 9524612..38b1879 100644
|
||||
index 9524612..c0f2070 100644
|
||||
--- a/net/freehaven/tor/control/TorControlConnection.java
|
||||
+++ b/net/freehaven/tor/control/TorControlConnection.java
|
||||
@@ -740,7 +740,7 @@ public class TorControlConnection implements TorControlCommands {
|
||||
@@ -736,11 +736,111 @@ public class TorControlConnection implements TorControlCommands {
|
||||
sendAndWaitForResponse("TAKEOWNERSHIP\r\n", null);
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * Tells Tor to generate and set up a new onion service using the best
|
||||
+ * supported algorithm.
|
||||
+ * <p/>
|
||||
+ * ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public Map<String,String> addOnion(Map<Integer,String> portLines)
|
||||
+ throws IOException {
|
||||
+ return addOnion("NEW:BEST", portLines, null);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tells Tor to generate and set up a new onion service using the best
|
||||
+ * supported algorithm.
|
||||
+ * <p/>
|
||||
+ * ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public Map<String,String> addOnion(Map<Integer,String> portLines,
|
||||
+ boolean ephemeral, boolean detach)
|
||||
+ throws IOException {
|
||||
+ return addOnion("NEW:BEST", portLines, ephemeral, detach);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tells Tor to set up an onion service using the provided private key.
|
||||
+ * <p/>
|
||||
+ * ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public Map<String,String> addOnion(String privKey,
|
||||
+ Map<Integer,String> portLines)
|
||||
+ throws IOException {
|
||||
+ return addOnion(privKey, portLines, null);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tells Tor to set up an onion service using the provided private key.
|
||||
+ * <p/>
|
||||
+ * ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public Map<String,String> addOnion(String privKey,
|
||||
+ Map<Integer,String> portLines,
|
||||
+ boolean ephemeral, boolean detach)
|
||||
+ throws IOException {
|
||||
+ List<String> flags = new ArrayList<String>();
|
||||
+ if (ephemeral)
|
||||
+ flags.add("DiscardPK");
|
||||
+ if (detach)
|
||||
+ flags.add("Detach");
|
||||
+ return addOnion(privKey, portLines, flags);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tells Tor to set up an onion service.
|
||||
+ * <p/>
|
||||
+ * ADD_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public Map<String,String> addOnion(String privKey,
|
||||
+ Map<Integer,String> portLines,
|
||||
+ List<String> flags)
|
||||
+ throws IOException {
|
||||
+ if (privKey.indexOf(':') < 0)
|
||||
+ throw new IllegalArgumentException("Invalid privKey");
|
||||
+ if (portLines == null || portLines.size() < 1)
|
||||
+ throw new IllegalArgumentException("Must provide at least one port line");
|
||||
+ StringBuilder b = new StringBuilder();
|
||||
+ b.append("ADD_ONION ").append(privKey);
|
||||
+ if (flags != null && flags.size() > 0) {
|
||||
+ b.append(" Flags=");
|
||||
+ String separator = "";
|
||||
+ for (String flag : flags) {
|
||||
+ b.append(separator).append(flag);
|
||||
+ separator = ",";
|
||||
+ }
|
||||
+ }
|
||||
+ for (Map.Entry<Integer,String> portLine : portLines.entrySet()) {
|
||||
+ int virtPort = portLine.getKey();
|
||||
+ String target = portLine.getValue();
|
||||
+ b.append(" Port=").append(virtPort);
|
||||
+ if (target != null && target.length() > 0)
|
||||
+ b.append(",").append(target);
|
||||
+ }
|
||||
+ b.append("\r\n");
|
||||
+ List<ReplyLine> lst = sendAndWaitForResponse(b.toString(), null);
|
||||
+ Map<String,String> ret = new HashMap<String,String>();
|
||||
+ ret.put(HS_ADDRESS, (lst.get(0)).msg.split("=", 2)[1]);
|
||||
+ if (lst.size() > 2)
|
||||
+ ret.put(HS_PRIVKEY, (lst.get(1)).msg.split("=", 2)[1]);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Tells Tor to take down an onion service previously set up with
|
||||
+ * addOnion(). The hostname excludes the .onion extension.
|
||||
+ * <p/>
|
||||
+ * DEL_ONION was added in Tor 0.2.7.1-alpha.
|
||||
+ */
|
||||
+ public void delOnion(String hostname) throws IOException {
|
||||
+ sendAndWaitForResponse("DEL_ONION " + hostname + "\r\n", null);
|
||||
+ }
|
||||
+
|
||||
/** Tells Tor to forget any cached client state relating to the hidden
|
||||
* service with the given hostname (excluding the .onion extension).
|
||||
*/
|
||||
public void forgetHiddenService(String hostname) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user