diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 1c388cbe8..b7539d0dc 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -51,7 +51,6 @@ 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; @@ -69,8 +68,6 @@ 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; import static org.briarproject.util.PrivacyUtils.scrubOnion; class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @@ -84,6 +81,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { 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}"); private static final Logger LOG = Logger.getLogger(TorPlugin.class.getName()); @@ -98,7 +96,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final int pollingInterval; private final ConnectionStatus connectionStatus; private final File torDirectory, torFile, geoIpFile, configFile; - private final File doneFile, cookieFile; + private final File doneFile, cookieFile, hostnameFile; private final PowerManager.WakeLock wakeLock; private final AtomicBoolean used = new AtomicBoolean(false); @@ -131,6 +129,7 @@ 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"); @@ -380,40 +379,48 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private void publishHiddenService(String port) { if (!running) return; - LOG.info("Creating hidden service"); - String privKey = callback.getSettings().get(HS_PRIVKEY); - Map portLines = - Collections.singletonMap(80, "127.0.0.1:" + port); - Map response; - try { - // 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; + 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 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 - String hostname = response.get(HS_ADDRESS); - if (LOG.isLoggable(INFO)) - LOG.info("Hidden service " + scrubOnion(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); + try { + String hostname = new String(read(hostnameFile), "UTF-8").trim(); + if (LOG.isLoggable(INFO)) + LOG.info("Hidden service " + scrubOnion(hostname)); + TransportProperties p = new TransportProperties(); + p.put(PROP_ONION, hostname.substring(0, 16)); + callback.mergeLocalProperties(p); + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } } diff --git a/briar-core/src/org/briarproject/util/PrivacyUtils.java b/briar-core/src/org/briarproject/util/PrivacyUtils.java index 247ccda3f..a87e72697 100644 --- a/briar-core/src/org/briarproject/util/PrivacyUtils.java +++ b/briar-core/src/org/briarproject/util/PrivacyUtils.java @@ -1,6 +1,5 @@ package org.briarproject.util; -import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -9,8 +8,7 @@ import java.net.SocketAddress; public class PrivacyUtils { public static String scrubOnion(String onion) { - // keep first three characters of onion address - return onion.substring(0, 3) + "[_scrubbed_]"; + return onion; } public static String scrubMacAddress(String address) {