From c3e6a80f78278e0c85318b083b454f66d0034dd4 Mon Sep 17 00:00:00 2001 From: jmacxx <47253594+jmacxx@users.noreply.github.com> Date: Tue, 12 Apr 2022 14:24:32 -0500 Subject: [PATCH] Add inbound Tor connectivity test. --- .../alert/PrivateNotificationManager.java | 52 ++++++++++++++++++- .../java/bisq/core/app/BisqHeadlessApp.java | 1 + .../main/java/bisq/core/app/BisqSetup.java | 27 ++++++++++ .../resources/i18n/displayStrings.properties | 3 ++ .../java/bisq/desktop/main/MainViewModel.java | 9 ++++ .../bisq/network/p2p/network/NetworkNode.java | 2 +- 6 files changed, 92 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/alert/PrivateNotificationManager.java b/core/src/main/java/bisq/core/alert/PrivateNotificationManager.java index 17bdf478595..f13b280944d 100644 --- a/core/src/main/java/bisq/core/alert/PrivateNotificationManager.java +++ b/core/src/main/java/bisq/core/alert/PrivateNotificationManager.java @@ -22,6 +22,11 @@ import bisq.network.p2p.P2PService; import bisq.network.p2p.SendMailboxMessageListener; import bisq.network.p2p.mailbox.MailboxMessageService; +import bisq.network.p2p.network.Connection; +import bisq.network.p2p.network.MessageListener; +import bisq.network.p2p.network.NetworkNode; +import bisq.network.p2p.peers.keepalive.messages.Ping; +import bisq.network.p2p.peers.keepalive.messages.Pong; import bisq.common.app.DevEnv; import bisq.common.config.Config; @@ -36,6 +41,10 @@ import javax.inject.Named; import com.google.common.base.Charsets; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; @@ -45,16 +54,20 @@ import java.math.BigInteger; +import java.util.Random; import java.util.UUID; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.jetbrains.annotations.NotNull; + import javax.annotation.Nullable; import static org.bitcoinj.core.Utils.HEX; -public class PrivateNotificationManager { +public class PrivateNotificationManager implements MessageListener { private static final Logger log = LoggerFactory.getLogger(PrivateNotificationManager.class); private final P2PService p2PService; @@ -69,6 +82,8 @@ public class PrivateNotificationManager { @Nullable private PrivateNotificationMessage privateNotificationMessage; + private final NetworkNode networkNode; + private Consumer pingResponseHandler = null; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, Initialization @@ -76,11 +91,13 @@ public class PrivateNotificationManager { @Inject public PrivateNotificationManager(P2PService p2PService, + NetworkNode networkNode, MailboxMessageService mailboxMessageService, KeyRing keyRing, @Named(Config.IGNORE_DEV_MSG) boolean ignoreDevMsg, @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) { this.p2PService = p2PService; + this.networkNode = networkNode; this.mailboxMessageService = mailboxMessageService; this.keyRing = keyRing; @@ -173,5 +190,38 @@ private boolean verifySignature(PrivateNotificationPayload privateNotification) } } + public void sendPing(NodeAddress peersNodeAddress, Consumer resultHandler) { + Ping ping = new Ping(new Random().nextInt(), 0); + log.info("Send Ping to peer {}, nonce={}", peersNodeAddress, ping.getNonce()); + SettableFuture future = networkNode.sendMessage(peersNodeAddress, ping); + Futures.addCallback(future, new FutureCallback<>() { + @Override + public void onSuccess(Connection connection) { + connection.addMessageListener(PrivateNotificationManager.this); + pingResponseHandler = resultHandler; + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + String errorMessage = "Sending ping to " + peersNodeAddress.getHostNameForDisplay() + + " failed. That is expected if the peer is offline.\n\tping=" + ping + + ".\n\tException=" + throwable.getMessage(); + log.info(errorMessage); + resultHandler.accept(errorMessage); + } + }, MoreExecutors.directExecutor()); + } + @Override + public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { + if (networkEnvelope instanceof Pong) { + Pong pong = (Pong) networkEnvelope; + String key = connection.getPeersNodeAddressOptional().get().getFullAddress(); + log.info("Received Pong! {} from {}", pong.toString(), key); + connection.removeMessageListener(this); + if (pingResponseHandler != null) { + pingResponseHandler.accept("SUCCESS"); + } + } + } } diff --git a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java index a6fcca5401c..9fb6abd8285 100644 --- a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java +++ b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java @@ -94,6 +94,7 @@ protected void setupHandlers() { bisqSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler")); bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList)); bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler")); + bisqSetup.setFirewallIssueHandler(() -> log.info("setFirewallIssueHandler")); bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported", lastVersion, Version.VERSION)); bisqSetup.setDaoRequiresRestartHandler(() -> { diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 02713b726c9..e51888f2a4f 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -22,6 +22,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.Alert; import bisq.core.alert.AlertManager; +import bisq.core.alert.PrivateNotificationManager; import bisq.core.alert.PrivateNotificationPayload; import bisq.core.btc.model.AddressEntry; import bisq.core.btc.nodes.LocalBitcoinNode; @@ -49,6 +50,7 @@ import bisq.core.util.coin.CoinFormatter; import bisq.network.Socks5ProxyProvider; +import bisq.network.p2p.NodeAddress; import bisq.network.p2p.P2PService; import bisq.network.p2p.storage.payload.PersistableNetworkPayload; import bisq.network.utils.Utils; @@ -132,6 +134,7 @@ default void onRequestWalletPassword() { private final WalletsSetup walletsSetup; private final BtcWalletService btcWalletService; private final P2PService p2PService; + private final PrivateNotificationManager privateNotificationManager; private final SignedWitnessStorageService signedWitnessStorageService; private final TradeManager tradeManager; private final OpenOfferManager openOfferManager; @@ -194,6 +197,9 @@ default void onRequestWalletPassword() { private Runnable qubesOSInfoHandler; @Setter @Nullable + private Runnable firewallIssueHandler; + @Setter + @Nullable private Runnable daoRequiresRestartHandler; @Setter @Nullable @@ -219,6 +225,7 @@ public BisqSetup(DomainInitialisation domainInitialisation, WalletsSetup walletsSetup, BtcWalletService btcWalletService, P2PService p2PService, + PrivateNotificationManager privateNotificationManager, SignedWitnessStorageService signedWitnessStorageService, TradeManager tradeManager, OpenOfferManager openOfferManager, @@ -242,6 +249,7 @@ public BisqSetup(DomainInitialisation domainInitialisation, this.walletsSetup = walletsSetup; this.btcWalletService = btcWalletService; this.p2PService = p2PService; + this.privateNotificationManager = privateNotificationManager; this.signedWitnessStorageService = signedWitnessStorageService; this.tradeManager = tradeManager; this.openOfferManager = openOfferManager; @@ -328,6 +336,7 @@ private void step4() { maybeShowLocalhostRunningInfo(); maybeShowAccountSigningStateInfo(); maybeShowTorAddressUpgradeInformation(); + checkTorFirewall(); } @@ -650,6 +659,24 @@ private void checkIfRunningOnQubesOS() { } } + /** + * If Bisq cannot connect to its own onion address through Tor, display + * an informative message to let the user know to configure their firewall else + * their offers will not be reachable. + */ + private void checkTorFirewall() { + NodeAddress onionAddress = p2PService.getNetworkNode().nodeAddressProperty().get(); + if (onionAddress == null || !onionAddress.getFullAddress().contains("onion")) { + return; + } + privateNotificationManager.sendPing(onionAddress, stringResult -> { + log.warn(stringResult); + if (stringResult.contains("failed")) { + firewallIssueHandler.run(); + } + }); + } + private void maybeShowSecurityRecommendation() { String key = "remindPasswordAndBackup"; user.getPaymentAccountsAsObservable().addListener((SetChangeListener) change -> { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 14679978e77..52b30ba72e5 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -3079,6 +3079,9 @@ popup.info.shutDownWithOpenOffers=Bisq is being shut down, but there are open of (i.e., make sure it doesn't go into standby mode...monitor standby is not a problem). popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\n\ Please make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes]. +popup.info.firewallSetupInfo=It appears this machine blocks incoming Tor connections. \ + This can happen in VM environments such as Qubes/VirtualBox/Whonix. \n\n\ + Please set up your environment to accept incoming Tor connections, otherwise no-one will be able to take your offers. popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version. popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue. diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index 17256fb2aab..c4a11020548 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -479,6 +479,15 @@ private void setupHandlers() { .show(); } }); + bisqSetup.setFirewallIssueHandler(() -> { + String key = "firewallSetupInfo"; + if (preferences.showAgain(key)) { + new Popup().information(Res.get("popup.info.firewallSetupInfo")) + .closeButtonText(Res.get("shared.iUnderstand")) + .dontShowAgainId(key) + .show(); + } + }); bisqSetup.setDownGradePreventionHandler(lastVersion -> { new Popup().warning(Res.get("popup.warn.downGradePrevention", lastVersion, Version.VERSION)) diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index fb5925991eb..9fca7006b5d 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -127,7 +127,7 @@ public SettableFuture sendMessage(@NotNull NodeAddress peersNodeAddr Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peersNodeAddress.getFullAddress()); if (peersNodeAddress.equals(getNodeAddress())) { - throw new ConnectException("We do not send a message to ourselves"); + log.warn("We are sending a message to ourselves"); } OutboundConnection outboundConnection = null;