mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 10:22:18 +01:00
Merge pull request #1935 from freimair/externalTor
Bisq can now use an external Tor service
This commit is contained in:
commit
e37813c658
@ -125,7 +125,10 @@ configure(project(':common')) {
|
||||
configure(project(':p2p')) {
|
||||
dependencies {
|
||||
compile project(':common')
|
||||
compile('com.github.JesusMcCloud.netlayer:tor.native:0.4.7.1.1') {
|
||||
compile('com.github.JesusMcCloud.netlayer:tor.native:0.6') {
|
||||
exclude(module: 'slf4j-api')
|
||||
}
|
||||
compile('com.github.JesusMcCloud.netlayer:tor.external:0.6') {
|
||||
exclude(module: 'slf4j-api')
|
||||
}
|
||||
compile('org.apache.httpcomponents:httpclient:4.5.3') {
|
||||
|
@ -193,9 +193,10 @@ public class BisqEnvironment extends StandardEnvironment {
|
||||
protected final String btcNodes, seedNodes, ignoreDevMsg, useDevPrivilegeKeys, useDevMode, useTorForBtc, rpcUser, rpcPassword,
|
||||
rpcPort, rpcBlockNotificationPort, dumpBlockchainData, fullDaoNode,
|
||||
myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress,
|
||||
torRcFile, torRcOptions,
|
||||
torRcFile, torRcOptions, externalTorControlPort, externalTorPassword, externalTorCookieFile,
|
||||
socks5ProxyHttpAddress, useAllProvidedNodes, numConnectionForBtc, genesisTxId, genesisBlockHeight, referralId, daoActivated;
|
||||
|
||||
protected final boolean externalTorUseSafeCookieAuthentication;
|
||||
|
||||
public BisqEnvironment(OptionSet options) {
|
||||
this(new JOptCommandLinePropertySource(BISQ_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
|
||||
@ -274,6 +275,18 @@ public class BisqEnvironment extends StandardEnvironment {
|
||||
torRcOptions = commandLineProperties.containsProperty(NetworkOptionKeys.TORRC_OPTIONS) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.TORRC_OPTIONS) :
|
||||
"";
|
||||
externalTorControlPort = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT) :
|
||||
"";
|
||||
externalTorPassword = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD) :
|
||||
"";
|
||||
externalTorCookieFile = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE) :
|
||||
"";
|
||||
externalTorUseSafeCookieAuthentication = commandLineProperties.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) ?
|
||||
true :
|
||||
false;
|
||||
|
||||
//RpcOptionKeys
|
||||
rpcUser = commandLineProperties.containsProperty(DaoOptionKeys.RPC_USER) ?
|
||||
@ -444,6 +457,11 @@ public class BisqEnvironment extends StandardEnvironment {
|
||||
setProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS, socks5ProxyHttpAddress);
|
||||
setProperty(NetworkOptionKeys.TORRC_FILE, torRcFile);
|
||||
setProperty(NetworkOptionKeys.TORRC_OPTIONS, torRcOptions);
|
||||
setProperty(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT, externalTorControlPort);
|
||||
setProperty(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD, externalTorPassword);
|
||||
setProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE, externalTorCookieFile);
|
||||
if (externalTorUseSafeCookieAuthentication)
|
||||
setProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE, "true");
|
||||
|
||||
setProperty(AppOptionKeys.APP_DATA_DIR_KEY, appDataDir);
|
||||
setProperty(AppOptionKeys.DESKTOP_WITH_HTTP_API, desktopWithHttpApi);
|
||||
|
@ -363,6 +363,24 @@ public abstract class BisqExecutable implements GracefulShutDownHandler {
|
||||
description("A list of torrc-entries to amend to Bisqs torrc. Note that torrc-entries, which are critical to Bisqs flawless operation, cannot be overwritten. [torrc options line, torrc option, ...]", ""))
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(RegexMatcher.regex("^([^\\s,]+\\s[^,]+,?\\s*)+$"));
|
||||
parser.accepts(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT,
|
||||
description("The control port of an already running Tor service to be used by Bisq [port].", ""))
|
||||
.availableUnless(NetworkOptionKeys.TORRC_FILE, NetworkOptionKeys.TORRC_OPTIONS)
|
||||
.withRequiredArg()
|
||||
.ofType(int.class);
|
||||
parser.accepts(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD,
|
||||
description("The password for controlling the already running Tor service.", ""))
|
||||
.availableIf(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT)
|
||||
.withRequiredArg();
|
||||
parser.accepts(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE,
|
||||
description("The cookie file for authenticating against the already running Tor service. Use in conjunction with --" + NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE, ""))
|
||||
.availableIf(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT)
|
||||
.availableUnless(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD)
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(new PathConverter(PathProperties.FILE_EXISTING, PathProperties.READABLE));
|
||||
parser.accepts(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE,
|
||||
description("Use the SafeCookie method when authenticating to the already running Tor service.", ""))
|
||||
.availableIf(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE);
|
||||
|
||||
//AppOptionKeys
|
||||
parser.accepts(AppOptionKeys.USER_DATA_DIR_KEY,
|
||||
|
@ -20,7 +20,8 @@ dependencyVerification {
|
||||
'de.jensd:fontawesomefx-commons:5539bb3335ecb822dbf928546f57766eeb9f1516cc1417a064b5709629612149',
|
||||
'com.googlecode.jcsv:jcsv:73ca7d715e90c8d2c2635cc284543b038245a34f70790660ed590e157b8714a2',
|
||||
'com.github.sarxos:webcam-capture:d960b7ea8ec3ddf2df0725ef214c3fccc9699ea7772df37f544e1f8e4fd665f6',
|
||||
'com.github.JesusMcCloud.netlayer:tor.native:0ad92f93c509a200a61cedbe0010d014f35ab57bcf131a4e268e1914e66be2e0',
|
||||
'com.github.JesusMcCloud.netlayer:tor.native:f1bf0096f9eb6020645a65d91aa530d15aef97e69cc5a79d7b2405421f74700a',
|
||||
'com.github.JesusMcCloud.netlayer:tor.external:cfba681398c191a1906d6d023a3be28a8fa9b1f4eee52e966daf7b1ae630414f',
|
||||
'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135',
|
||||
'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342',
|
||||
'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf',
|
||||
@ -37,11 +38,11 @@ dependencyVerification {
|
||||
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
|
||||
'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8',
|
||||
'com.google.inject:guice:9b9df27a5b8c7864112b4137fd92b36c3f1395bfe57be42fedf2f520ead1a93e',
|
||||
'com.github.JesusMcCloud.netlayer:tor:4a6a6102331c35e7ad2a574cf81ddab89fc1256305805e82c5af1f542f336629',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:b306e0e6735841e31e320bf3260c71d60fc35057cfa87895f23251ee260a64a8',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:169ee5879cba8444499243ceea5e6a2cb6ecea5424211cc819f0704501154b35',
|
||||
'com.github.JesusMcCloud.netlayer:tor:ac8465b7dda30ea920ec31a6bde42df7e88bee0282e805ce2797628938e3cf0b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:193ab7813e4d249f2ea4fc1b968fea8c2126bcbeeb5d6127050ce1b93dbaa7c2',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:877b59bbe466b24a88275a71fd06cd97359d2085420f6f1ac1d766afa8116001',
|
||||
'io.github.microutils:kotlin-logging:4992504fd3c6ecdf9ed10874b9508e758bb908af9e9d7af19a61e9afb6b7e27a',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:f0595b9ed88ddc6fd66bddf68c56c6f2f6c4b17faa51e43e478acad32b05303e',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:4ff0fcb97f4983b4aaba12668c24ad21b08460915db1b021d8f1d8bee687f21c',
|
||||
'org.jetbrains:annotations:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.bouncycastle:bcpg-jdk15on:de3355b821fc81dd32e1f3f560d5b3eca1c678fd2400011d0bfc69fb91bcde85',
|
||||
'commons-io:commons-io:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
|
||||
@ -63,14 +64,13 @@ dependencyVerification {
|
||||
'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4',
|
||||
'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729',
|
||||
'com.cedricwalter:tor-binary-geoip:fbd7656a262607e5a73016e048d5270cbabcd4639a1795b4b4e762df8877429d',
|
||||
'com.github.JesusMcCloud:jtorctl:c6ef92e46074d8d26db718ce0fe4b64b8cf7b934b7377d164c5d613b4cd7b847',
|
||||
'org.apache.commons:commons-compress:a778bbd659722889245fc52a0ec2873fbbb89ec661bc1ad3dc043c0757c784c4',
|
||||
'com.github.JesusMcCloud:jtorctl:ba71601cbe50474ccc39a17bc6f7880c1412d8d19b94d37aee69ea2917f72046',
|
||||
'org.apache.commons:commons-compress:5f2df1e467825e4cac5996d44890c4201c000b43c0b23cffc0782d28a0beb9b0',
|
||||
'org.tukaani:xz:a594643d73cc01928cf6ca5ce100e094ea9d73af760a5d4fb6b75fa673ecec96',
|
||||
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
|
||||
'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||
'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080',
|
||||
'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707',
|
||||
'org.objenesis:objenesis:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
|
||||
'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
|
||||
]
|
||||
}
|
||||
|
@ -31,4 +31,8 @@ public class NetworkOptionKeys {
|
||||
public static final String SOCKS_5_PROXY_HTTP_ADDRESS = "socks5ProxyHttpAddress";
|
||||
public static final String TORRC_OPTIONS = "torrcOptions";
|
||||
public static final String TORRC_FILE = "torrcFile";
|
||||
public static final String EXTERNAL_TOR_CONTROL_PORT = "torControlPort";
|
||||
public static final String EXTERNAL_TOR_PASSWORD = "torControlPassword";
|
||||
public static final String EXTERNAL_TOR_COOKIE_FILE = "torControlCookieFile";
|
||||
public static final String EXTERNAL_TOR_USE_SAFECOOKIE = "torControlUseSafeCookieAuth";
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import bisq.network.NetworkOptionKeys;
|
||||
import bisq.network.p2p.network.BridgeAddressProvider;
|
||||
import bisq.network.p2p.network.LocalhostNetworkNode;
|
||||
import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.network.NewTor;
|
||||
import bisq.network.p2p.network.RunningTor;
|
||||
import bisq.network.p2p.network.TorNetworkNode;
|
||||
|
||||
import bisq.common.proto.network.NetworkProtoResolver;
|
||||
@ -44,10 +46,17 @@ public class NetworkNodeProvider implements Provider<NetworkNode> {
|
||||
@Named(NetworkOptionKeys.PORT_KEY) int port,
|
||||
@Named(NetworkOptionKeys.TOR_DIR) File torDir,
|
||||
@Named(NetworkOptionKeys.TORRC_FILE) String torrcFile,
|
||||
@Named(NetworkOptionKeys.TORRC_OPTIONS) String torrcOptions) {
|
||||
@Named(NetworkOptionKeys.TORRC_OPTIONS) String torrcOptions,
|
||||
@Named(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT) String controlPort,
|
||||
@Named(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD) String password,
|
||||
@Named(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE) String cookieFile,
|
||||
@Named(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) boolean useSafeCookieAuthentication ) {
|
||||
networkNode = useLocalhostForP2P ?
|
||||
new LocalhostNetworkNode(address, port, networkProtoResolver) :
|
||||
new TorNetworkNode(port, torDir, networkProtoResolver, bridgeAddressProvider, torrcFile, torrcOptions);
|
||||
new TorNetworkNode(port, torDir, networkProtoResolver,
|
||||
!controlPort.isEmpty() ?
|
||||
new RunningTor(torDir, Integer.parseInt(controlPort), password, cookieFile, useSafeCookieAuthentication) :
|
||||
new NewTor(torDir, torrcFile, torrcOptions, bridgeAddressProvider.getBridgeAddresses()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,5 +90,9 @@ public class P2PModule extends AppModule {
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.TORRC_FILE)).to(environment.getRequiredProperty(NetworkOptionKeys.TORRC_FILE));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.TORRC_OPTIONS)).to(environment.getRequiredProperty(NetworkOptionKeys.TORRC_OPTIONS));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_CONTROL_PORT));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_PASSWORD));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE)).to(environment.getRequiredProperty(NetworkOptionKeys.EXTERNAL_TOR_COOKIE_FILE));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE)).to(environment.containsProperty(NetworkOptionKeys.EXTERNAL_TOR_USE_SAFECOOKIE) ? true : false);
|
||||
}
|
||||
}
|
||||
|
121
p2p/src/main/java/bisq/network/p2p/network/NewTor.java
Normal file
121
p2p/src/main/java/bisq/network/p2p/network/NewTor.java
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.network;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class creates a brand new instance of the Tor onion router.
|
||||
*
|
||||
* When asked, the class checks, whether command line parameters such as
|
||||
* --torrcFile and --torrcOptions are set and if so, takes these settings into
|
||||
* account. Then, a fresh set of Tor binaries is installed and Tor is launched.
|
||||
* Finally, a {@link Tor} instance is returned for further use.
|
||||
*
|
||||
* @author Florian Reimair
|
||||
*
|
||||
*/
|
||||
public class NewTor extends TorMode {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(NewTor.class);
|
||||
|
||||
private final String torrcFile;
|
||||
private final String torrcOptions;
|
||||
private final Collection<String> bridgeEntries;
|
||||
private final File torWorkikngDirectory;
|
||||
|
||||
public NewTor(File torWorkingDirectory, String torrcFile, String torrcOptions, Collection<String> bridgeEntries) {
|
||||
this.torrcFile = torrcFile;
|
||||
this.torrcOptions = torrcOptions;
|
||||
this.bridgeEntries = bridgeEntries;
|
||||
this.torWorkikngDirectory = torWorkingDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tor getTor() throws IOException, TorCtlException {
|
||||
long ts1 = new Date().getTime();
|
||||
|
||||
if (bridgeEntries != null)
|
||||
log.info("Using bridges: {}", bridgeEntries.stream().collect(Collectors.joining(",")));
|
||||
|
||||
Torrc override = null;
|
||||
|
||||
// check if the user wants to provide his own torrc file
|
||||
if (!"".equals(torrcFile)) {
|
||||
try {
|
||||
override = new Torrc(new FileInputStream(new File(torrcFile)));
|
||||
} catch (IOException e) {
|
||||
log.error("custom torrc file not found ('{}'). Proceeding with defaults.", torrcFile);
|
||||
}
|
||||
}
|
||||
|
||||
// check if the user wants to temporarily add to the default torrc file
|
||||
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
|
||||
if (!"".equals(torrcOptions)) {
|
||||
Arrays.asList(torrcOptions.split(",")).forEach(line -> {
|
||||
line = line.trim();
|
||||
if (line.matches("^[^\\s]+\\s.+")) {
|
||||
String[] tmp = line.split("\\s", 2);
|
||||
torrcOptionsMap.put(tmp[0].trim(), tmp[1].trim());
|
||||
} else {
|
||||
log.error("custom torrc override parse error ('{}'). Proceeding without custom overrides.", line);
|
||||
torrcOptionsMap.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// assemble final override options
|
||||
if (!torrcOptionsMap.isEmpty())
|
||||
// check for custom torrcFile
|
||||
if (override != null)
|
||||
// and merge the contents
|
||||
override = new Torrc(override.getInputStream$tor_native(), torrcOptionsMap);
|
||||
else
|
||||
override = new Torrc(torrcOptionsMap);
|
||||
|
||||
log.info("Starting tor");
|
||||
NativeTor result = new NativeTor(torWorkikngDirectory, bridgeEntries, override);
|
||||
log.info(
|
||||
"\n################################################################\n"
|
||||
+ "Tor started after {} ms. Start publishing hidden service.\n"
|
||||
+ "################################################################",
|
||||
(new Date().getTime() - ts1)); // takes usually a few seconds
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHiddenServiceDirectory() {
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
87
p2p/src/main/java/bisq/network/p2p/network/RunningTor.java
Normal file
87
p2p/src/main/java/bisq/network/p2p/network/RunningTor.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.network;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.berndpruenster.netlayer.tor.ExternalTor;
|
||||
import org.berndpruenster.netlayer.tor.Tor;
|
||||
import org.berndpruenster.netlayer.tor.TorCtlException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class creates a brand new instance of the Tor onion router.
|
||||
*
|
||||
* When asked, the class checks for the authentication method selected and
|
||||
* connects to the given control port. Finally, a {@link Tor} instance is
|
||||
* returned for further use.
|
||||
*
|
||||
* @author Florian Reimair
|
||||
*
|
||||
*/
|
||||
public class RunningTor extends TorMode {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RunningTor.class);
|
||||
private final int controlPort;
|
||||
private final String password;
|
||||
private final String torDir;
|
||||
private final File cookieFile;
|
||||
private final boolean useSafeCookieAuthentication;
|
||||
|
||||
|
||||
public RunningTor(final File torDir, final int controlPort, final String password, final String cookieFile,
|
||||
final boolean useSafeCookieAuthentication) {
|
||||
this.torDir = torDir.getAbsolutePath();
|
||||
this.controlPort = controlPort;
|
||||
this.password = password;
|
||||
this.cookieFile = new File(cookieFile);
|
||||
this.useSafeCookieAuthentication = useSafeCookieAuthentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tor getTor() throws IOException, TorCtlException {
|
||||
long ts1 = new Date().getTime();
|
||||
|
||||
log.info("Connecting to running tor");
|
||||
|
||||
Tor result;
|
||||
if (!password.isEmpty())
|
||||
result = new ExternalTor(controlPort, password);
|
||||
else if (cookieFile.exists())
|
||||
result = new ExternalTor(controlPort, cookieFile, useSafeCookieAuthentication);
|
||||
else
|
||||
result = new ExternalTor(controlPort);
|
||||
|
||||
log.info(
|
||||
"\n################################################################\n"
|
||||
+ "Tor started after {} ms. Start publishing hidden service.\n"
|
||||
+ "################################################################",
|
||||
(new Date().getTime() - ts1)); // takes usually a few seconds
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHiddenServiceDirectory() {
|
||||
return torDir + File.separator + "externalTorHiddenService";
|
||||
}
|
||||
|
||||
}
|
60
p2p/src/main/java/bisq/network/p2p/network/TorMode.java
Normal file
60
p2p/src/main/java/bisq/network/p2p/network/TorMode.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.network.p2p.network;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.berndpruenster.netlayer.tor.Tor;
|
||||
import org.berndpruenster.netlayer.tor.TorCtlException;
|
||||
|
||||
/**
|
||||
* Holds information on how tor should be created and delivers a respective
|
||||
* {@link Tor} object when asked.
|
||||
*
|
||||
* @author Florian Reimair
|
||||
*
|
||||
*/
|
||||
public abstract class TorMode {
|
||||
|
||||
/**
|
||||
* Returns a fresh {@link Tor} object.
|
||||
*
|
||||
* @param torDir points to the place, where we will persist private key and
|
||||
* address data
|
||||
* @return a fresh instance of {@link Tor}
|
||||
* @throws IOException
|
||||
* @throws TorCtlException
|
||||
*/
|
||||
public abstract Tor getTor() throws IOException, TorCtlException;
|
||||
|
||||
/**
|
||||
* {@link NativeTor}'s inner workings prepend its Tor installation path and some
|
||||
* other stuff to the hiddenServiceDir, thus, selecting nothing (i.e.
|
||||
* <code>""</code>) as a hidden service directory is fine. {@link ExternalTor},
|
||||
* however, does not have a Tor installation path and thus, takes the hidden
|
||||
* service path literally. Hence, we set
|
||||
* <code>"torDir/ephemeralHiddenService"</code> as the hidden service directory.
|
||||
*
|
||||
* @return <code>""</code> in {@link NewTor} Mode,
|
||||
* <code>"torDir/ephemeralHiddenService"</code> in {@link RunningTor}
|
||||
* mode
|
||||
*/
|
||||
public abstract String getHiddenServiceDirectory();
|
||||
|
||||
}
|
@ -28,11 +28,9 @@ import bisq.common.storage.FileUtil;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.berndpruenster.netlayer.tor.HiddenServiceSocket;
|
||||
import org.berndpruenster.netlayer.tor.NativeTor;
|
||||
import org.berndpruenster.netlayer.tor.Tor;
|
||||
import org.berndpruenster.netlayer.tor.TorCtlException;
|
||||
import org.berndpruenster.netlayer.tor.TorSocket;
|
||||
import org.berndpruenster.netlayer.tor.Torrc;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
@ -52,11 +50,8 @@ import java.net.Socket;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
@ -80,27 +75,22 @@ public class TorNetworkNode extends NetworkNode {
|
||||
|
||||
private HiddenServiceSocket hiddenServiceSocket;
|
||||
private final File torDir;
|
||||
private final BridgeAddressProvider bridgeAddressProvider;
|
||||
private Timer shutDownTimeoutTimer;
|
||||
private int restartCounter;
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private MonadicBinding<Boolean> allShutDown;
|
||||
private Tor tor;
|
||||
|
||||
private String torrcFile = "";
|
||||
private String torrcOptions = "";
|
||||
|
||||
private TorMode torMode;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public TorNetworkNode(int servicePort, File torDir, NetworkProtoResolver networkProtoResolver, BridgeAddressProvider bridgeAddressProvider, String torrcFile, String torrcOptions) {
|
||||
public TorNetworkNode(int servicePort, File torDir, NetworkProtoResolver networkProtoResolver, TorMode torMode) {
|
||||
super(servicePort, networkProtoResolver);
|
||||
this.torDir = torDir;
|
||||
this.bridgeAddressProvider = bridgeAddressProvider;
|
||||
this.torrcFile = torrcFile;
|
||||
this.torrcOptions = torrcOptions;
|
||||
this.torMode = torMode;
|
||||
}
|
||||
|
||||
|
||||
@ -119,7 +109,7 @@ public class TorNetworkNode extends NetworkNode {
|
||||
createExecutorService();
|
||||
|
||||
// Create the tor node (takes about 6 sec.)
|
||||
createTorAndHiddenService(torDir, Utils.findFreeSystemPort(), servicePort, bridgeAddressProvider.getBridgeAddresses());
|
||||
createTorAndHiddenService(Utils.findFreeSystemPort(), servicePort);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -244,62 +234,18 @@ public class TorNetworkNode extends NetworkNode {
|
||||
// create tor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void createTorAndHiddenService(File torDir, int localPort, int servicePort, @Nullable List<String> bridgeEntries) {
|
||||
private void createTorAndHiddenService(int localPort, int servicePort) {
|
||||
Log.traceCall();
|
||||
if (bridgeEntries != null)
|
||||
log.info("Using bridges: {}", bridgeEntries.stream().collect(Collectors.joining(",")));
|
||||
|
||||
ListenableFuture<Void> future = executorService.submit(() -> {
|
||||
try {
|
||||
long ts1 = new Date().getTime();
|
||||
|
||||
Torrc override = null;
|
||||
|
||||
// check if the user wants to provide his own torrc file
|
||||
if(!"".equals(torrcFile)) {
|
||||
try {
|
||||
override = new Torrc(new FileInputStream(new File(torrcFile)));
|
||||
} catch(IOException e) {
|
||||
log.error("custom torrc file not found ('{}'). Proceeding with defaults.", torrcFile);
|
||||
}
|
||||
}
|
||||
|
||||
// check if the user wants to temporarily add to the default torrc file
|
||||
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
|
||||
if(!"".equals(torrcOptions)) {
|
||||
Arrays.asList(torrcOptions.split(",")).forEach(line -> {
|
||||
line = line.trim();
|
||||
if(line.matches("^[^\\s]+\\s.+")) {
|
||||
String[] tmp = line.split("\\s", 2);
|
||||
torrcOptionsMap.put(tmp[0].trim(), tmp[1].trim());
|
||||
}
|
||||
else {
|
||||
log.error("custom torrc override parse error ('{}'). Proceeding without custom overrides.", line);
|
||||
torrcOptionsMap.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// assemble final override options
|
||||
if(!torrcOptionsMap.isEmpty())
|
||||
// check for custom torrcFile
|
||||
if(override != null)
|
||||
// and merge the contents
|
||||
override = new Torrc(override.getInputStream$tor(), torrcOptionsMap);
|
||||
else
|
||||
override = new Torrc(torrcOptionsMap);
|
||||
|
||||
log.info("Starting tor");
|
||||
Tor.setDefault(new NativeTor(torDir, bridgeEntries, override));
|
||||
log.info("\n################################################################\n" +
|
||||
"Tor started after {} ms. Start publishing hidden service.\n" +
|
||||
"################################################################",
|
||||
(new Date().getTime() - ts1)); // takes usually a few seconds
|
||||
|
||||
// get tor
|
||||
Tor.setDefault(torMode.getTor());
|
||||
UserThread.execute(() -> setupListeners.stream().forEach(SetupListener::onTorNodeReady));
|
||||
|
||||
// start hidden service
|
||||
long ts2 = new Date().getTime();
|
||||
hiddenServiceSocket = new HiddenServiceSocket(localPort, "", servicePort);
|
||||
hiddenServiceSocket = new HiddenServiceSocket(localPort, torMode.getHiddenServiceDirectory(), servicePort);
|
||||
hiddenServiceSocket.addReadyListener(socket -> {
|
||||
try {
|
||||
log.info("\n################################################################\n" +
|
||||
@ -330,6 +276,14 @@ public class TorNetworkNode extends NetworkNode {
|
||||
} catch (TorCtlException e) {
|
||||
log.error("Tor node creation failed: " + (e.getCause() != null ? e.getCause().toString() : e.toString()));
|
||||
restartTor(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.error("Could not connect to running Tor: "
|
||||
+ e.getMessage());
|
||||
|
||||
// Seems a bit harsh, but since we cannot connect to Tor, we cannot do nothing
|
||||
// furthermore, we have no hidden services started yet, so there is no graceful
|
||||
// shutdown needed either
|
||||
System.exit(1);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -53,7 +53,9 @@ public class TorNetworkNodeTest {
|
||||
public void testTorNodeBeforeSecondReady() throws InterruptedException, IOException {
|
||||
latch = new CountDownLatch(1);
|
||||
int port = 9001;
|
||||
TorNetworkNode node1 = new TorNetworkNode(port, new File("torNode_" + port), TestUtils.getNetworkProtoResolver(), null, "", "");
|
||||
TorNetworkNode node1 = new TorNetworkNode(port, new File("torNode_" + port),
|
||||
TestUtils.getNetworkProtoResolver(),
|
||||
new NewTor(new File("torNode_" + port), "", "", new ArrayList<String>()));
|
||||
node1.start(new SetupListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
@ -79,7 +81,9 @@ public class TorNetworkNodeTest {
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
int port2 = 9002;
|
||||
TorNetworkNode node2 = new TorNetworkNode(port2, new File("torNode_" + port2), TestUtils.getNetworkProtoResolver(), null, "", "");
|
||||
TorNetworkNode node2 = new TorNetworkNode(port2, new File("torNode_" + port2),
|
||||
TestUtils.getNetworkProtoResolver(),
|
||||
new NewTor(new File("torNode_" + port), "", "", new ArrayList<String>()));
|
||||
node2.start(new SetupListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
@ -136,7 +140,9 @@ public class TorNetworkNodeTest {
|
||||
public void testTorNodeAfterBothReady() throws InterruptedException, IOException {
|
||||
latch = new CountDownLatch(2);
|
||||
int port = 9001;
|
||||
TorNetworkNode node1 = new TorNetworkNode(port, new File("torNode_" + port), TestUtils.getNetworkProtoResolver(), null, "", "");
|
||||
TorNetworkNode node1 = new TorNetworkNode(port, new File("torNode_" + port),
|
||||
TestUtils.getNetworkProtoResolver(),
|
||||
new NewTor(new File("torNode_" + port), "", "", new ArrayList<String>()));
|
||||
node1.start(new SetupListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
@ -161,7 +167,9 @@ public class TorNetworkNodeTest {
|
||||
});
|
||||
|
||||
int port2 = 9002;
|
||||
TorNetworkNode node2 = new TorNetworkNode(port2, new File("torNode_" + port), TestUtils.getNetworkProtoResolver(), null, "", "");
|
||||
TorNetworkNode node2 = new TorNetworkNode(port2, new File("torNode_" + port),
|
||||
TestUtils.getNetworkProtoResolver(),
|
||||
new NewTor(new File("torNode_" + port), "", "", new ArrayList<String>()));
|
||||
node2.start(new SetupListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
|
Loading…
Reference in New Issue
Block a user