diff --git a/build.gradle b/build.gradle index 692bfad741..c2e4bf5da1 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ configure(subprojects) { loggingVersion = '1.2' lombokVersion = '1.18.30' mockitoVersion = '5.10.0' - netlayerVersion = '6797461310f077bbea4f43a3a509c077b0ed8c34' // Netlayer version 0.7.3 with Tor browser version 11.0.14 and tor binary version: 0.4.7.7 + netlayerVersion = '2b459dc' // Tor browser version 11.5.2 and tor binary version: 0.4.7.10 protobufVersion = '3.19.1' protocVersion = protobufVersion pushyVersion = '0.13.2' diff --git a/common/src/main/java/haveno/common/config/Config.java b/common/src/main/java/haveno/common/config/Config.java index 4edd9e9295..4682293f18 100644 --- a/common/src/main/java/haveno/common/config/Config.java +++ b/common/src/main/java/haveno/common/config/Config.java @@ -84,6 +84,7 @@ public class Config { public static final String USE_TOR_FOR_XMR = "useTorForXmr"; public static final String TORRC_FILE = "torrcFile"; public static final String TORRC_OPTIONS = "torrcOptions"; + public static final String TOR_CONTROL_HOST = "torControlHost"; public static final String TOR_CONTROL_PORT = "torControlPort"; public static final String TOR_CONTROL_PASSWORD = "torControlPassword"; public static final String TOR_CONTROL_COOKIE_FILE = "torControlCookieFile"; @@ -173,6 +174,7 @@ public class Config { public final String socks5ProxyHttpAddress; public final File torrcFile; public final String torrcOptions; + public final String torControlHost; public final int torControlPort; public final String torControlPassword; public final File torControlCookieFile; @@ -446,6 +448,11 @@ public class Config { .withValuesConvertedBy(RegexMatcher.regex("^([^\\s,]+\\s[^,]+,?\\s*)+$")) .defaultsTo(""); + ArgumentAcceptingOptionSpec torControlHostOpt = + parser.accepts(TOR_CONTROL_HOST, "The control hostname of an already running Tor service to be used by Haveno.") + .withRequiredArg() + .defaultsTo("127.0.0.1"); + ArgumentAcceptingOptionSpec torControlPortOpt = parser.accepts(TOR_CONTROL_PORT, "The control port of an already running Tor service to be used by Haveno.") @@ -667,6 +674,7 @@ public class Config { this.bitcoinRegtestHost = options.valueOf(bitcoinRegtestHostOpt); this.torrcFile = options.has(torrcFileOpt) ? options.valueOf(torrcFileOpt).toFile() : null; this.torrcOptions = options.valueOf(torrcOptionsOpt); + this.torControlHost = options.valueOf(torControlHostOpt); this.torControlPort = options.valueOf(torControlPortOpt); this.torControlPassword = options.valueOf(torControlPasswordOpt); this.torControlCookieFile = options.has(torControlCookieFileOpt) ? diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 8e7d17d8f5..2163a4bfb3 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -236,6 +236,11 @@ + + + + + @@ -260,6 +265,11 @@ + + + + + @@ -284,6 +294,11 @@ + + + + + @@ -315,6 +330,11 @@ + + + + + @@ -339,6 +359,11 @@ + + + + + @@ -363,6 +388,11 @@ + + + + + @@ -387,6 +417,11 @@ + + + + + @@ -411,6 +446,11 @@ + + + + + diff --git a/p2p/src/main/java/haveno/network/p2p/NetworkNodeProvider.java b/p2p/src/main/java/haveno/network/p2p/NetworkNodeProvider.java index 4027abb2e2..676db83fbd 100644 --- a/p2p/src/main/java/haveno/network/p2p/NetworkNodeProvider.java +++ b/p2p/src/main/java/haveno/network/p2p/NetworkNodeProvider.java @@ -47,6 +47,7 @@ public class NetworkNodeProvider implements Provider { @Named(Config.TOR_DIR) File torDir, @Nullable @Named(Config.TORRC_FILE) File torrcFile, @Named(Config.TORRC_OPTIONS) String torrcOptions, + @Named(Config.TOR_CONTROL_HOST) String controlHost, @Named(Config.TOR_CONTROL_PORT) int controlPort, @Named(Config.TOR_CONTROL_PASSWORD) String password, @Nullable @Named(Config.TOR_CONTROL_COOKIE_FILE) File cookieFile, @@ -59,11 +60,12 @@ public class NetworkNodeProvider implements Provider { torDir, torrcFile, torrcOptions, + controlHost, controlPort, password, cookieFile, useSafeCookieAuthentication); - networkNode = new TorNetworkNode(port, networkProtoResolver, streamIsolation, torMode, banFilter, maxConnections); + networkNode = new TorNetworkNode(port, networkProtoResolver, streamIsolation, torMode, banFilter, maxConnections, controlHost); } } @@ -71,12 +73,13 @@ public class NetworkNodeProvider implements Provider { File torDir, @Nullable File torrcFile, String torrcOptions, + String controlHost, int controlPort, String password, @Nullable File cookieFile, boolean useSafeCookieAuthentication) { return controlPort != Config.UNSPECIFIED_PORT ? - new RunningTor(torDir, controlPort, password, cookieFile, useSafeCookieAuthentication) : + new RunningTor(torDir, controlHost, controlPort, password, cookieFile, useSafeCookieAuthentication) : new NewTor(torDir, torrcFile, torrcOptions, bridgeAddressProvider); } diff --git a/p2p/src/main/java/haveno/network/p2p/P2PModule.java b/p2p/src/main/java/haveno/network/p2p/P2PModule.java index 5bb0f1ef30..50ce7d5642 100644 --- a/p2p/src/main/java/haveno/network/p2p/P2PModule.java +++ b/p2p/src/main/java/haveno/network/p2p/P2PModule.java @@ -19,8 +19,26 @@ package haveno.network.p2p; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; +import static com.google.inject.name.Names.named; +import static com.google.inject.util.Providers.of; import haveno.common.app.AppModule; import haveno.common.config.Config; +import static haveno.common.config.Config.BAN_LIST; +import static haveno.common.config.Config.MAX_CONNECTIONS; +import static haveno.common.config.Config.NODE_PORT; +import static haveno.common.config.Config.REPUBLISH_MAILBOX_ENTRIES; +import static haveno.common.config.Config.SOCKS_5_PROXY_HTTP_ADDRESS; +import static haveno.common.config.Config.SOCKS_5_PROXY_XMR_ADDRESS; +import static haveno.common.config.Config.TORRC_FILE; +import static haveno.common.config.Config.TORRC_OPTIONS; +import static haveno.common.config.Config.TOR_CONTROL_COOKIE_FILE; +import static haveno.common.config.Config.TOR_CONTROL_HOST; +import static haveno.common.config.Config.TOR_CONTROL_PASSWORD; +import static haveno.common.config.Config.TOR_CONTROL_PORT; +import static haveno.common.config.Config.TOR_CONTROL_USE_SAFE_COOKIE_AUTH; +import static haveno.common.config.Config.TOR_DIR; +import static haveno.common.config.Config.TOR_STREAM_ISOLATION; +import static haveno.common.config.Config.USE_LOCALHOST_FOR_P2P; import haveno.network.Socks5ProxyProvider; import haveno.network.http.HttpClient; import haveno.network.http.HttpClientImpl; @@ -35,29 +53,10 @@ import haveno.network.p2p.storage.P2PDataStorage; import haveno.network.p2p.storage.persistence.AppendOnlyDataStoreService; import haveno.network.p2p.storage.persistence.ProtectedDataStoreService; import haveno.network.p2p.storage.persistence.ResourceDataStoreService; - import java.io.File; import java.time.Clock; import java.util.List; -import static com.google.inject.name.Names.named; -import static com.google.inject.util.Providers.of; -import static haveno.common.config.Config.BAN_LIST; -import static haveno.common.config.Config.MAX_CONNECTIONS; -import static haveno.common.config.Config.NODE_PORT; -import static haveno.common.config.Config.REPUBLISH_MAILBOX_ENTRIES; -import static haveno.common.config.Config.SOCKS_5_PROXY_XMR_ADDRESS; -import static haveno.common.config.Config.SOCKS_5_PROXY_HTTP_ADDRESS; -import static haveno.common.config.Config.TORRC_FILE; -import static haveno.common.config.Config.TORRC_OPTIONS; -import static haveno.common.config.Config.TOR_CONTROL_COOKIE_FILE; -import static haveno.common.config.Config.TOR_CONTROL_PASSWORD; -import static haveno.common.config.Config.TOR_CONTROL_PORT; -import static haveno.common.config.Config.TOR_CONTROL_USE_SAFE_COOKIE_AUTH; -import static haveno.common.config.Config.TOR_DIR; -import static haveno.common.config.Config.TOR_STREAM_ISOLATION; -import static haveno.common.config.Config.USE_LOCALHOST_FOR_P2P; - public class P2PModule extends AppModule { public P2PModule(Config config) { @@ -96,6 +95,7 @@ public class P2PModule extends AppModule { bindConstant().annotatedWith(named(SOCKS_5_PROXY_HTTP_ADDRESS)).to(config.socks5ProxyHttpAddress); bind(File.class).annotatedWith(named(TORRC_FILE)).toProvider(of(config.torrcFile)); // allow null value bindConstant().annotatedWith(named(TORRC_OPTIONS)).to(config.torrcOptions); + bindConstant().annotatedWith(named(TOR_CONTROL_HOST)).to(config.torControlHost); bindConstant().annotatedWith(named(TOR_CONTROL_PORT)).to(config.torControlPort); bindConstant().annotatedWith(named(TOR_CONTROL_PASSWORD)).to(config.torControlPassword); bind(File.class).annotatedWith(named(TOR_CONTROL_COOKIE_FILE)).toProvider(of(config.torControlCookieFile)); diff --git a/p2p/src/main/java/haveno/network/p2p/network/RunningTor.java b/p2p/src/main/java/haveno/network/p2p/network/RunningTor.java index e0cd2f8378..eb2011d16a 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/RunningTor.java +++ b/p2p/src/main/java/haveno/network/p2p/network/RunningTor.java @@ -17,15 +17,13 @@ package haveno.network.p2p.network; +import java.io.File; +import java.util.Date; import lombok.extern.slf4j.Slf4j; import org.berndpruenster.netlayer.tor.ExternalTor; import org.berndpruenster.netlayer.tor.Tor; import org.berndpruenster.netlayer.tor.TorCtlException; -import java.io.File; -import java.io.IOException; -import java.util.Date; - /** * This class creates a brand new instance of the Tor onion router. * @@ -39,15 +37,21 @@ import java.util.Date; @Slf4j public class RunningTor extends TorMode { + private final String controlHost; private final int controlPort; private final String password; private final File cookieFile; private final boolean useSafeCookieAuthentication; - public RunningTor(final File torDir, final int controlPort, final String password, final File cookieFile, - final boolean useSafeCookieAuthentication) { + public RunningTor(final File torDir, + final String controlHost, + final int controlPort, + final String password, + final File cookieFile, + final boolean useSafeCookieAuthentication) { super(torDir); + this.controlHost = controlHost; this.controlPort = controlPort; this.password = password; this.cookieFile = cookieFile; @@ -55,18 +59,18 @@ public class RunningTor extends TorMode { } @Override - public Tor getTor() throws IOException, TorCtlException { + public Tor getTor() throws TorCtlException { long ts1 = new Date().getTime(); log.info("Connecting to running tor"); Tor result; if (!password.isEmpty()) - result = new ExternalTor(controlPort, password); + result = new ExternalTor(controlHost, controlPort, password); else if (cookieFile != null && cookieFile.exists()) - result = new ExternalTor(controlPort, cookieFile, useSafeCookieAuthentication); + result = new ExternalTor(controlHost, controlPort, cookieFile, useSafeCookieAuthentication); else - result = new ExternalTor(controlPort); + result = new ExternalTor(controlHost, controlPort); log.info( "\n################################################################\n" diff --git a/p2p/src/main/java/haveno/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/haveno/network/p2p/network/TorNetworkNode.java index f3047250c8..58842ed1f6 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/haveno/network/p2p/network/TorNetworkNode.java @@ -51,6 +51,8 @@ import static com.google.common.base.Preconditions.checkArgument; public class TorNetworkNode extends NetworkNode { private static final long SHUT_DOWN_TIMEOUT = 2; + private final String torControlHost; + private HiddenServiceSocket hiddenServiceSocket; private Timer shutDownTimeoutTimer; private Tor tor; @@ -70,10 +72,11 @@ public class TorNetworkNode extends NetworkNode { boolean useStreamIsolation, TorMode torMode, @Nullable BanFilter banFilter, - int maxConnections) { + int maxConnections, String torControlHost) { super(servicePort, networkProtoResolver, banFilter, maxConnections); this.torMode = torMode; this.streamIsolation = useStreamIsolation; + this.torControlHost = torControlHost; executor = SingleThreadExecutorUtils.getSingleThreadExecutor("StartTor"); } @@ -97,7 +100,7 @@ public class TorNetworkNode extends NetworkNode { checkArgument(peerNodeAddress.getHostName().endsWith(".onion"), "PeerAddress is not an onion address"); // If streamId is null stream isolation gets deactivated. // Hidden services use stream isolation by default, so we pass null. - return new TorSocket(peerNodeAddress.getHostName(), peerNodeAddress.getPort(), null); + return new TorSocket(peerNodeAddress.getHostName(), peerNodeAddress.getPort(), torControlHost, null); } public Socks5Proxy getSocksProxy() { @@ -111,7 +114,7 @@ public class TorNetworkNode extends NetworkNode { if (socksProxy == null || streamIsolation) { tor = Tor.getDefault(); - socksProxy = tor != null ? tor.getProxy(stream) : null; + socksProxy = tor != null ? tor.getProxy(torControlHost, stream) : null; } return socksProxy; } catch (Throwable t) { diff --git a/p2p/src/test/java/haveno/network/p2p/network/TorNetworkNodeTest.java b/p2p/src/test/java/haveno/network/p2p/network/TorNetworkNodeTest.java index a35476509a..f74a9d341a 100644 --- a/p2p/src/test/java/haveno/network/p2p/network/TorNetworkNodeTest.java +++ b/p2p/src/test/java/haveno/network/p2p/network/TorNetworkNodeTest.java @@ -51,7 +51,7 @@ public class TorNetworkNodeTest { latch = new CountDownLatch(1); int port = 9001; TorNetworkNode node1 = new TorNetworkNode(port, TestUtils.getNetworkProtoResolver(), false, - new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12); + new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12, "127.0.0.1"); node1.start(new SetupListener() { @Override public void onTorNodeReady() { @@ -78,7 +78,7 @@ public class TorNetworkNodeTest { latch = new CountDownLatch(1); int port2 = 9002; TorNetworkNode node2 = new TorNetworkNode(port2, TestUtils.getNetworkProtoResolver(), false, - new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12); + new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12, "127.0.0.1"); node2.start(new SetupListener() { @Override public void onTorNodeReady() { @@ -136,7 +136,7 @@ public class TorNetworkNodeTest { latch = new CountDownLatch(2); int port = 9001; TorNetworkNode node1 = new TorNetworkNode(port, TestUtils.getNetworkProtoResolver(), false, - new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12); + new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12, "127.0.0.1"); node1.start(new SetupListener() { @Override public void onTorNodeReady() { @@ -162,7 +162,7 @@ public class TorNetworkNodeTest { int port2 = 9002; TorNetworkNode node2 = new TorNetworkNode(port2, TestUtils.getNetworkProtoResolver(), false, - new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12); + new NewTor(new File("torNode_" + port), null, "", this::getBridgeAddresses), null, 12, "127.0.0.1"); node2.start(new SetupListener() { @Override public void onTorNodeReady() {