From c18a2f43882e8f4c9440877bb6201c90c76ced0f Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Thu, 7 Oct 2021 12:23:28 +0200 Subject: [PATCH] improve tor identity management --- .../sparrowwallet/sparrow/TorLogHandler.java | 5 ++ .../sparrow/control/MixStatusCell.java | 15 +++++- .../sparrowwallet/sparrow/net/TorService.java | 29 +++++++++-- .../sparrow/whirlpool/Whirlpool.java | 3 +- .../tor/SparrowTorClientService.java | 49 +++++++++++++++++++ .../sparrow/preferences/server.fxml | 4 +- 6 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/whirlpool/tor/SparrowTorClientService.java diff --git a/src/main/java/com/sparrowwallet/sparrow/TorLogHandler.java b/src/main/java/com/sparrowwallet/sparrow/TorLogHandler.java index aa6e6e2f..53adafa6 100644 --- a/src/main/java/com/sparrowwallet/sparrow/TorLogHandler.java +++ b/src/main/java/com/sparrowwallet/sparrow/TorLogHandler.java @@ -2,11 +2,16 @@ package com.sparrowwallet.sparrow; import com.sparrowwallet.drongo.LogHandler; import com.sparrowwallet.sparrow.event.TorStatusEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.event.Level; public class TorLogHandler implements LogHandler { + private static final Logger log = LoggerFactory.getLogger(TorLogHandler.class); + @Override public void handleLog(String threadName, Level level, String message, String loggerName, long timestamp, StackTraceElement[] callerData) { + log.debug(message); EventManager.get().post(new TorStatusEvent(message)); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java index d551e0e9..f15d57a2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MixStatusCell.java @@ -13,6 +13,7 @@ import com.sparrowwallet.sparrow.whirlpool.WhirlpoolException; import javafx.geometry.Pos; import javafx.scene.control.*; import org.controlsfx.glyphfont.Glyph; +import org.controlsfx.tools.Platform; public class MixStatusCell extends TreeTableCell { public MixStatusCell() { @@ -68,7 +69,8 @@ public class MixStatusCell extends TreeTableCell { Tooltip tt = new Tooltip(); tt.setText(mixFailReason.getMessage() + (mixError == null ? "" : ": " + mixError) + "\nMix failures are generally caused by peers disconnecting during a mix." + - "\nMake sure your internet connection is stable and the computer is configured to prevent sleeping."); + "\nMake sure your internet connection is stable and the computer is configured to prevent sleeping." + + "\nTo prevent sleeping, use the " + getPlatformSleepConfig() + " or enable the function in the Tools menu."); setTooltip(tt); } else { setGraphic(null); @@ -76,6 +78,17 @@ public class MixStatusCell extends TreeTableCell { } } + private String getPlatformSleepConfig() { + Platform platform = Platform.getCurrent(); + if(platform == Platform.OSX) { + return "OSX System Preferences"; + } else if(platform == Platform.WINDOWS) { + return "Windows Control Panel"; + } + + return "system power settings"; + } + private void setMixProgress(MixProgress mixProgress) { if(mixProgress.getMixStep() != MixStep.FAIL) { ProgressIndicator progressIndicator = getProgressIndicator(); diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TorService.java b/src/main/java/com/sparrowwallet/sparrow/net/TorService.java index 764d7eff..4d497b07 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TorService.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TorService.java @@ -3,15 +3,14 @@ package com.sparrowwallet.sparrow.net; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import net.freehaven.tor.control.TorControlError; -import org.berndpruenster.netlayer.tor.NativeTor; -import org.berndpruenster.netlayer.tor.Tor; -import org.berndpruenster.netlayer.tor.TorCtlException; -import org.berndpruenster.netlayer.tor.Torrc; +import org.berndpruenster.netlayer.tor.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; +import java.net.Socket; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; @@ -40,6 +39,7 @@ public class TorService extends ScheduledService { try { LinkedHashMap torrcOptionsMap = new LinkedHashMap<>(); torrcOptionsMap.put("SocksPort", Integer.toString(PROXY_PORT)); + torrcOptionsMap.put("HashedControlPassword", "16:D780432418F09B06609940000924317D3B9DF522A3191F8F4E597E9329"); torrcOptionsMap.put("DisableNetwork", "0"); Torrc override = new Torrc(torrcOptionsMap); @@ -62,4 +62,25 @@ public class TorService extends ScheduledService { } }; } + + public static Socket getControlSocket() { + Tor tor = Tor.getDefault(); + if(tor != null) { + try { + Class torClass = Class.forName("org.berndpruenster.netlayer.tor.Tor"); + Field torControllerField = torClass.getDeclaredField("torController"); + torControllerField.setAccessible(true); + TorController torController = (TorController)torControllerField.get(tor); + + Class torControllerClass = Class.forName("org.berndpruenster.netlayer.tor.TorController"); + Field socketField = torControllerClass.getDeclaredField("socket"); + socketField.setAccessible(true); + return (Socket)socketField.get(torController); + } catch(Exception e) { + log.error("Error retrieving Tor control socket", e); + } + } + + return null; + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java index 7cfb0756..0e979c21 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/Whirlpool.java @@ -42,6 +42,7 @@ import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowMinerFeeSupplier; import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowPostmixHandler; +import com.sparrowwallet.sparrow.whirlpool.tor.SparrowTorClientService; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -87,7 +88,7 @@ public class Whirlpool { this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase()); this.httpClientService = new JavaHttpClientService(torProxy); this.stompClientService = new JavaStompClientService(httpClientService); - this.torClientService = new WhirlpoolTorClientService(); + this.torClientService = new SparrowTorClientService(this); this.whirlpoolWalletService = new WhirlpoolWalletService(); this.config = computeWhirlpoolWalletConfig(torProxy); diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/tor/SparrowTorClientService.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/tor/SparrowTorClientService.java new file mode 100644 index 00000000..829327a7 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/tor/SparrowTorClientService.java @@ -0,0 +1,49 @@ +package com.sparrowwallet.sparrow.whirlpool.tor; + +import com.google.common.net.HostAndPort; +import com.samourai.tor.client.TorClientService; +import com.sparrowwallet.sparrow.net.TorService; +import com.sparrowwallet.sparrow.whirlpool.Whirlpool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.Socket; + +public class SparrowTorClientService extends TorClientService { + private static final Logger log = LoggerFactory.getLogger(SparrowTorClientService.class); + + private final Whirlpool whirlpool; + + public SparrowTorClientService(Whirlpool whirlpool) { + this.whirlpool = whirlpool; + } + + @Override + public void changeIdentity() { + HostAndPort proxy = whirlpool.getTorProxy(); + if(proxy != null) { + Socket controlSocket = TorService.getControlSocket(); + if(controlSocket != null) { + try { + writeNewNym(controlSocket); + } catch(Exception e) { + log.warn("Error sending NEWNYM to " + controlSocket, e); + } + } else { + HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1); + try(Socket socket = new Socket(control.getHost(), control.getPort())) { + writeNewNym(socket); + } catch(Exception e) { + log.warn("Error connecting to " + control + ", no Tor ControlPort configured?"); + } + } + } + } + + private void writeNewNym(Socket socket) throws IOException { + log.debug("Sending NEWNYM to " + socket); + socket.getOutputStream().write("AUTHENTICATE \"\"\r\n".getBytes()); + socket.getOutputStream().write("SIGNAL NEWNYM\r\n".getBytes()); + } +} diff --git a/src/main/resources/com/sparrowwallet/sparrow/preferences/server.fxml b/src/main/resources/com/sparrowwallet/sparrow/preferences/server.fxml index e5139ef9..0021a012 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/preferences/server.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/preferences/server.fxml @@ -145,7 +145,7 @@ - + @@ -172,7 +172,7 @@ - +