Update to master of jtorproxy

This commit is contained in:
Manfred Karrer 2015-11-17 23:54:57 +01:00
parent cefcf5b3d8
commit 9b1108aa0a
30 changed files with 1043 additions and 924 deletions

View file

@ -13,7 +13,9 @@ See the Apache 2 License for the specific language governing permissions and lim
package com.msopentech.thali.java.toronionproxy;
import com.msopentech.thali.toronionproxy.FileUtilities;
import com.msopentech.thali.toronionproxy.OnionProxyContext;
import com.msopentech.thali.toronionproxy.OsData;
import com.msopentech.thali.toronionproxy.WriteObserver;
import java.io.File;
@ -42,8 +44,61 @@ public class JavaOnionProxyContext extends OnionProxyContext {
@Override
public String getProcessId() {
// This is a horrible hack. It seems like more JVMs will return the process's PID this way, but not guarantees.
// This is a horrible hack. It seems like more JVMs will return the
// process's PID this way, but not guarantees.
String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
return processName.split("@")[0];
}
@Override
public void installFiles() throws IOException, InterruptedException {
super.installFiles();
switch (OsData.getOsType()) {
case Windows:
case Linux32:
case Linux64:
case Mac:
FileUtilities.extractContentFromZip(getWorkingDirectory(),
getAssetOrResourceByName(getPathToTorExecutable() + "tor.zip"));
break;
default:
throw new RuntimeException("We don't support Tor on this OS yet");
}
}
@Override
protected String getPathToTorExecutable() {
String path = "native/";
switch (OsData.getOsType()) {
case Windows:
return path + "windows/x86/"; // We currently only support the
// x86 build but that should work
// everywhere
case Mac:
return path + "osx/x64/"; // I don't think there even is a x32
// build of Tor for Mac, but could be
// wrong.
case Linux32:
return path + "linux/x86/";
case Linux64:
return path + "linux/x64/";
default:
throw new RuntimeException("We don't support Tor on this OS");
}
}
@Override
protected String getTorExecutableFileName() {
switch (OsData.getOsType()) {
case Linux32:
case Linux64:
return "tor";
case Windows:
return "tor.exe";
case Mac:
return "tor.real";
default:
throw new RuntimeException("We don't support Tor on this OS");
}
}
}

View file

@ -25,7 +25,6 @@ public class FileUtilities {
/**
* Closes both input and output streams when done.
*
* @param in Stream to read from
* @param out Stream to write to
* @throws java.io.IOException - If close on input or output fails
@ -40,7 +39,6 @@ public class FileUtilities {
/**
* Won't close the input stream when it's done, needed to handle ZipInputStreams
*
* @param in Won't be closed
* @param out Will be closed
* @throws java.io.IOException - If close on output fails
@ -86,7 +84,6 @@ public class FileUtilities {
/**
* Reads the input stream, deletes fileToWriteTo if it exists and over writes it with the stream.
*
* @param readFrom Stream to read from
* @param fileToWriteTo File to write to
* @throws java.io.IOException - If any of the file operations fail
@ -113,7 +110,6 @@ public class FileUtilities {
/**
* This has to exist somewhere! Why isn't it a part of the standard Java library?
*
* @param destinationDirectory Directory files are to be extracted to
* @param zipFileInputStream Stream to unzip
* @throws java.io.IOException - If there are any file errors

View file

@ -44,11 +44,10 @@ abstract public class OnionProxyContext {
torrcFile = new File(getWorkingDirectory(), torrcName);
torExecutableFile = new File(getWorkingDirectory(), getTorExecutableFileName());
cookieFile = new File(getWorkingDirectory(), ".tor/control_auth_cookie");
hostnameFile = new File(getWorkingDirectory(), "/" + hiddenserviceDirectoryName
+ "/hostname");
hostnameFile = new File(getWorkingDirectory(), "/" + hiddenserviceDirectoryName + "/hostname");
}
public void installFiles() throws IOException, InterruptedException {
protected void installFiles() throws IOException, InterruptedException {
// This is sleezy but we have cases where an old instance of the Tor OP
// needs an extra second to
// clean itself up. Without that time we can't do things like delete its
@ -56,7 +55,13 @@ abstract public class OnionProxyContext {
// do by default, something we hope to fix with
// https://github.com/thaliproject/Tor_Onion_Proxy_Library/issues/13
Thread.sleep(1000, 0);
if (getWorkingDirectory().listFiles() != null) {
for (File f : getWorkingDirectory().listFiles()) {
if (f.getAbsolutePath().startsWith(torrcFile.getAbsolutePath())) {
f.delete();
}
}
}
try {
File dotTorDir = new File(getWorkingDirectory(), ".tor");
if (dotTorDir.exists())
@ -70,30 +75,15 @@ abstract public class OnionProxyContext {
FileUtilities.cleanInstallOneFile(getAssetOrResourceByName(geoIpName), geoIpFile);
FileUtilities.cleanInstallOneFile(getAssetOrResourceByName(geoIpv6Name), geoIpv6File);
FileUtilities.cleanInstallOneFile(getAssetOrResourceByName(torrcName), torrcFile);
switch (OsData.getOsType()) {
case Android:
FileUtilities.cleanInstallOneFile(getAssetOrResourceByName(getPathToTorExecutable()
+ getTorExecutableFileName()), torExecutableFile);
break;
case Windows:
case Linux32:
case Linux64:
case Mac:
FileUtilities.extractContentFromZip(getWorkingDirectory(),
getAssetOrResourceByName(getPathToTorExecutable() + "tor.zip"));
break;
default:
throw new RuntimeException("We don't support Tor on this OS yet");
}
}
/**
* Sets environment variables and working directory needed for Tor
*
* @param processBuilder we will call start on this to run Tor
* @param processBuilder
* we will call start on this to run Tor
*/
public void setEnvironmentArgsAndWorkingDirectoryForStart(ProcessBuilder processBuilder) {
void setEnvironmentArgsAndWorkingDirectoryForStart(ProcessBuilder processBuilder) {
processBuilder.directory(getWorkingDirectory());
Map<String, String> environment = processBuilder.environment();
environment.put("HOME", getWorkingDirectory().getAbsolutePath());
@ -153,7 +143,7 @@ abstract public class OnionProxyContext {
return workingDirectory;
}
public void deleteAllFilesButHiddenServices() throws InterruptedException {
void deleteAllFilesButHiddenServices() throws InterruptedException {
// It can take a little bit for the Tor OP to detect the connection is
// dead and kill itself
Thread.sleep(1000);
@ -177,42 +167,9 @@ abstract public class OnionProxyContext {
*
* @return Path to executable in JAR Resources
*/
protected String getPathToTorExecutable() {
String path = "native/";
switch (OsData.getOsType()) {
case Android:
return "";
case Windows:
return path + "windows/x86/"; // We currently only support the
// x86 build but that should work
// everywhere
case Mac:
return path + "osx/x64/"; // I don't think there even is a x32
// build of Tor for Mac, but could be
// wrong.
case Linux32:
return path + "linux/x86/";
case Linux64:
return path + "linux/x64/";
default:
throw new RuntimeException("We don't support Tor on this OS");
}
}
protected abstract String getPathToTorExecutable();
protected String getTorExecutableFileName() {
switch (OsData.getOsType()) {
case Android:
case Linux32:
case Linux64:
return "tor";
case Windows:
return "tor.exe";
case Mac:
return "tor.real";
default:
throw new RuntimeException("We don't support Tor on this OS");
}
}
protected abstract String getTorExecutableFileName();
abstract public String getProcessId();

View file

@ -29,6 +29,8 @@ See the Apache 2 License for the specific language governing permissions and lim
package com.msopentech.thali.toronionproxy;
import io.nucleo.net.HiddenServiceDescriptor;
import io.nucleo.net.HiddenServiceReadyListener;
import net.freehaven.tor.control.ConfigEntry;
import net.freehaven.tor.control.TorControlConnection;
import org.slf4j.Logger;
@ -44,19 +46,20 @@ import java.util.concurrent.CountDownLatch;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* This is where all the fun is, this is the class that handles the heavy work. Note that you will most likely need
* to actually call into the AndroidOnionProxyManager or JavaOnionProxyManager in order to create the right bindings
* for your environment.
* <p>
* This class is thread safe but that's mostly because we hit everything over the head with 'synchronized'. Given the
* way this class is used there shouldn't be any performance implications of this.
* <p>
* This is where all the fun is, this is the class that handles the heavy work.
* Note that you will most likely need to actually call into the
* AndroidOnionProxyManager or JavaOnionProxyManager in order to create the
* right bindings for your environment.
* <p/>
* This class is thread safe but that's mostly because we hit everything over
* the head with 'synchronized'. Given the way this class is used there
* shouldn't be any performance implications of this.
* <p/>
* This class began life as TorPlugin from the Briar Project
*/
public abstract class OnionProxyManager {
private static final String[] EVENTS = {
"CIRC", "ORCONN", "NOTICE", "WARN", "ERR"
};
private static final String[] EVENTS = {"CIRC", "WARN", "ERR"};
private static final String[] EVENTS_HS = {"EXTENDED", "CIRC", "ORCONN", "INFO", "NOTICE", "WARN", "ERR", "HS_DESC"};
private static final String OWNER = "__OwningControllerProcess";
private static final int COOKIE_TIMEOUT = 3 * 1000; // Milliseconds
@ -65,32 +68,50 @@ public abstract class OnionProxyManager {
protected final OnionProxyContext onionProxyContext;
public OnionProxyContext getOnionProxyContext() {
return onionProxyContext;
}
private volatile Socket controlSocket = null;
// If controlConnection is not null then this means that a connection exists and the Tor OP will die when
// If controlConnection is not null then this means that a connection exists
// and the Tor OP will die when
// the connection fails.
private volatile TorControlConnection controlConnection = null;
private volatile int control_port;
private OnionProxyManagerEventHandler eventHandler;
public OnionProxyManager(OnionProxyContext onionProxyContext) {
this.onionProxyContext = onionProxyContext;
eventHandler = new OnionProxyManagerEventHandler();
}
public void attachHiddenServiceReadyListener(HiddenServiceDescriptor hs, HiddenServiceReadyListener listener) {
eventHandler.setHStoWatchFor(hs, listener);
}
/**
* This is a blocking call that will try to start the Tor OP, connect it to the network and get it to be fully
* bootstrapped. Sometimes the bootstrap process just hangs for no apparent reason so the method will wait for the
* given time for bootstrap to finish and if it doesn't then will restart the bootstrap process the given number of
* repeats.
* This is a blocking call that will try to start the Tor OP, connect it to
* the network and get it to be fully bootstrapped. Sometimes the bootstrap
* process just hangs for no apparent reason so the method will wait for the
* given time for bootstrap to finish and if it doesn't then will restart
* the bootstrap process the given number of repeats.
*
* @param secondsBeforeTimeOut Seconds to wait for boot strapping to finish
* @param numberOfRetries Number of times to try recycling the Tor OP before giving up on bootstrapping working
* @return True if bootstrap succeeded, false if there is a problem or the bootstrap couldn't complete in the given
* time.
* @throws java.lang.InterruptedException - You know, if we are interrupted
* @throws java.io.IOException - IO Exceptions
* @param secondsBeforeTimeOut
* Seconds to wait for boot strapping to finish
* @param numberOfRetries
* Number of times to try recycling the Tor OP before giving up
* on bootstrapping working
* @return True if bootstrap succeeded, false if there is a problem or the
* bootstrap couldn't complete in the given time.
* @throws java.lang.InterruptedException
* - You know, if we are interrupted
* @throws java.io.IOException
* - IO Exceptions
*/
public synchronized boolean startWithRepeat(int secondsBeforeTimeOut, int numberOfRetries) throws
InterruptedException, IOException {
public synchronized boolean startWithRepeat(int secondsBeforeTimeOut, int numberOfRetries)
throws InterruptedException, IOException {
if (secondsBeforeTimeOut <= 0 || numberOfRetries < 0) {
throw new IllegalArgumentException("secondsBeforeTimeOut >= 0 & numberOfRetries > 0");
}
@ -102,7 +123,8 @@ public abstract class OnionProxyManager {
}
enableNetwork(true);
// We will check every second to see if boot strapping has finally finished
// We will check every second to see if boot strapping has
// finally finished
for (int secondsWaited = 0; secondsWaited < secondsBeforeTimeOut; ++secondsWaited) {
if (isBootstrapped() == false) {
Thread.sleep(1000, 0);
@ -113,18 +135,24 @@ public abstract class OnionProxyManager {
// Bootstrapping isn't over so we need to restart and try again
stop();
// Experimentally we have found that if a Tor OP has run before and thus has cached descriptors
// and that when we try to start it again it won't start then deleting the cached data can fix this.
// But, if there is cached data and things do work then the Tor OP will start faster than it would
// Experimentally we have found that if a Tor OP has run before and thus
// has cached descriptors
// and that when we try to start it again it won't start then deleting
// the cached data can fix this.
// But, if there is cached data and things do work then the Tor OP will
// start faster than it would
// if we delete everything.
// So our compromise is that we try to start the Tor OP 'as is' on the first round and after that
// So our compromise is that we try to start the Tor OP 'as is' on the
// first round and after that
// we delete all the files.
onionProxyContext.deleteAllFilesButHiddenServices();
}
return false;
} finally {
// Make sure we return the Tor OP in some kind of consistent state, even if it's 'off'.
// Make sure we return the Tor OP in some kind of consistent state,
// even if it's 'off'.
if (isRunning() == false) {
stop();
}
@ -132,22 +160,26 @@ public abstract class OnionProxyManager {
}
/**
* Returns the socks port on the IPv4 localhost address that the Tor OP is listening on
* Returns the socks port on the IPv4 localhost address that the Tor OP is
* listening on
*
* @return Discovered socks port
* @throws java.io.IOException - File errors
* @throws java.io.IOException
* - File errors
*/
public synchronized int getIPv4LocalHostSocksPort() throws IOException {
if (isRunning() == false) {
throw new RuntimeException("Tor is not running!");
}
// This returns a set of space delimited quoted strings which could be Ipv4, Ipv6 or unix sockets
// This returns a set of space delimited quoted strings which could be
// Ipv4, Ipv6 or unix sockets
String[] socksIpPorts = controlConnection.getInfo("net/listeners/socks").split(" ");
for (String address : socksIpPorts) {
if (address.contains("\"127.0.0.1:")) {
// Remember, the last character will be a " so we have to remove that
// Remember, the last character will be a " so we have to remove
// that
return Integer.parseInt(address.substring(address.lastIndexOf(":") + 1, address.length() - 1));
}
}
@ -158,10 +190,14 @@ public abstract class OnionProxyManager {
/**
* Publishes a hidden service
*
* @param hiddenServicePort The port that the hidden service will accept connections on
* @param localPort The local port that the hidden service will relay connections to
* @param hiddenServicePort
* The port that the hidden service will accept connections on
* @param localPort
* The local port that the hidden service will relay connections
* to
* @return The hidden service's onion address in the form X.onion.
* @throws java.io.IOException - File errors
* @throws java.io.IOException
* - File errors
*/
public synchronized String publishHiddenService(int hiddenServicePort, int localPort) throws IOException {
if (controlConnection == null) {
@ -170,20 +206,19 @@ public abstract class OnionProxyManager {
List<ConfigEntry> currentHiddenServices = controlConnection.getConf("HiddenServiceOptions");
if ((currentHiddenServices.size() == 1 &&
currentHiddenServices.get(0).key.compareTo("HiddenServiceOptions") == 0 &&
currentHiddenServices.get(0).value.compareTo("") == 0) == false) {
throw new RuntimeException("Sorry, only one hidden service to a customer and we already have one. Please " +
"send complaints to https://github" +
".com/thaliproject/Tor_Onion_Proxy_Library/issues/5 with your scenario so we can justify fixing " +
"this.");
if ((currentHiddenServices.size() == 1
&& currentHiddenServices.get(0).key.compareTo("HiddenServiceOptions") == 0
&& currentHiddenServices.get(0).value.compareTo("") == 0) == false) {
throw new RuntimeException("Sorry, only one hidden service to a customer and we already have one. Please "
+ "send complaints to https://github"
+ ".com/thaliproject/Tor_Onion_Proxy_Library/issues/5 with your scenario so we can justify fixing "
+ "this.");
}
LOG.info("Creating hidden service");
File hostnameFile = onionProxyContext.getHostNameFile();
if (hostnameFile.getParentFile().exists() == false &&
hostnameFile.getParentFile().mkdirs() == false) {
if (hostnameFile.getParentFile().exists() == false && hostnameFile.getParentFile().mkdirs() == false) {
throw new RuntimeException("Could not create hostnameFile parent directory");
}
@ -193,16 +228,14 @@ public abstract class OnionProxyManager {
// Thanks, Ubuntu!
try {
switch (OsData.getOsType()) {
case Mac:
case Android:
case Linux32:
case Linux64: {
case Linux64:
case Mac: {
Set<PosixFilePermission> perms = new HashSet<>();
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_EXECUTE);
Files.setPosixFilePermissions(onionProxyContext.getHiddenServiceDirectory()
.toPath(), perms);
Files.setPosixFilePermissions(onionProxyContext.getHiddenServiceDirectory().toPath(), perms);
}
default:
break;
@ -211,11 +244,12 @@ public abstract class OnionProxyManager {
} catch (Exception e) {
e.printStackTrace();
}
controlConnection.setEvents(Arrays.asList(EVENTS_HS));
// Watch for the hostname file being created/updated
WriteObserver hostNameFileObserver = onionProxyContext.generateWriteObserver(hostnameFile);
// Use the control connection to update the Tor config
List<String> config = Arrays.asList(
"HiddenServiceDir " + hostnameFile.getParentFile().getAbsolutePath(),
List<String> config = Arrays.asList("HiddenServiceDir " + hostnameFile.getParentFile().getAbsolutePath(),
"HiddenServicePort " + hiddenServicePort + " 127.0.0.1:" + localPort);
controlConnection.setConf(config);
controlConnection.saveConf();
@ -232,11 +266,24 @@ public abstract class OnionProxyManager {
return hostname;
}
public synchronized boolean isHiddenServiceAvailable(String onionurl) {
try {
return controlConnection.isHSAvailable(onionurl.substring(0, onionurl.indexOf(".")));
} catch (IOException e) {
// We'll have to wait for tor 0.2.7
e.printStackTrace();
System.err.println("We'll have to wait for Tor 0.2.7 for HSFETCH to work!");
}
return false;
}
/**
* Kills the Tor OP Process. Once you have called this method nothing is going to work until you either call
* startWithRepeat or installAndStartTorOp
* Kills the Tor OP Process. Once you have called this method nothing is
* going to work until you either call startWithRepeat or
* installAndStartTorOp
*
* @throws java.io.IOException - File errors
* @throws java.io.IOException
* - File errors
*/
public synchronized void stop() throws IOException {
try {
@ -256,10 +303,12 @@ public abstract class OnionProxyManager {
}
/**
* Checks to see if the Tor OP is running (e.g. fully bootstrapped) and open to network connections.
* Checks to see if the Tor OP is running (e.g. fully bootstrapped) and open
* to network connections.
*
* @return True if running
* @throws java.io.IOException - IO exceptions
* @throws java.io.IOException
* - IO exceptions
*/
public synchronized boolean isRunning() throws IOException {
return isBootstrapped() && isNetworkEnabled();
@ -268,8 +317,11 @@ public abstract class OnionProxyManager {
/**
* Tells the Tor OP if it should accept network connections
*
* @param enable If true then the Tor OP will accept SOCKS connections, otherwise not.
* @throws java.io.IOException - IO exceptions
* @param enable
* If true then the Tor OP will accept SOCKS connections,
* otherwise not.
* @throws java.io.IOException
* - IO exceptions
*/
public synchronized void enableNetwork(boolean enable) throws IOException {
if (controlConnection == null) {
@ -282,9 +334,10 @@ public abstract class OnionProxyManager {
/**
* Specifies if Tor OP is accepting network connections
*
* @return True if network is enabled (that doesn't mean that the device is online, only that the Tor OP is trying
* to connect to the network)
* @throws java.io.IOException - IO exceptions
* @return True if network is enabled (that doesn't mean that the device is
* online, only that the Tor OP is trying to connect to the network)
* @throws java.io.IOException
* - IO exceptions
*/
public synchronized boolean isNetworkEnabled() throws IOException {
if (controlConnection == null) {
@ -293,7 +346,8 @@ public abstract class OnionProxyManager {
List<ConfigEntry> disableNetworkSettingValues = controlConnection.getConf("DisableNetwork");
boolean result = false;
// It's theoretically possible for us to get multiple values back, if even one is false then we will
// It's theoretically possible for us to get multiple values back, if
// even one is false then we will
// assume all are false
for (ConfigEntry configEntry : disableNetworkSettingValues) {
if (configEntry.value.equals("1")) {
@ -323,7 +377,7 @@ public abstract class OnionProxyManager {
}
if (phase != null && phase.contains("PROGRESS=100")) {
LOG.trace("Tor has already bootstrapped");
LOG.info("Tor has already bootstrapped");
return true;
}
@ -331,23 +385,29 @@ public abstract class OnionProxyManager {
}
/**
* Installs all necessary files and starts the Tor OP in offline mode (e.g. networkEnabled(false)). This would
* only be used if you wanted to start the Tor OP so that the install and related is all done but aren't ready to
* actually connect it to the network.
* Installs all necessary files and starts the Tor OP in offline mode (e.g.
* networkEnabled(false)). This would only be used if you wanted to start
* the Tor OP so that the install and related is all done but aren't ready
* to actually connect it to the network.
*
* @return True if all files installed and Tor OP successfully started
* @throws java.io.IOException - IO Exceptions
* @throws java.lang.InterruptedException - If we are, well, interrupted
* @throws java.io.IOException
* - IO Exceptions
* @throws java.lang.InterruptedException
* - If we are, well, interrupted
*/
public synchronized boolean installAndStartTorOp() throws IOException, InterruptedException {
// The Tor OP will die if it looses the connection to its socket so if there is no controlSocket defined
// then Tor is dead. This assumes, of course, that takeOwnership works and we can't end up with Zombies.
// The Tor OP will die if it looses the connection to its socket so if
// there is no controlSocket defined
// then Tor is dead. This assumes, of course, that takeOwnership works
// and we can't end up with Zombies.
if (controlConnection != null) {
LOG.info("Tor is already running");
return true;
}
// The code below is why this method is synchronized, we don't want two instances of it running at once
// The code below is why this method is synchronized, we don't want two
// instances of it running at once
// as the result would be a mess of screwed up files and connections.
LOG.info("Tor is not running");
@ -355,13 +415,14 @@ public abstract class OnionProxyManager {
LOG.info("Starting Tor");
File cookieFile = onionProxyContext.getCookieFile();
if (cookieFile.getParentFile().exists() == false &&
cookieFile.getParentFile().mkdirs() == false) {
if (cookieFile.getParentFile().exists() == false && cookieFile.getParentFile().mkdirs() == false) {
throw new RuntimeException("Could not create cookieFile parent directory");
}
// The original code from Briar watches individual files, not a directory and Android's file observer
// won't work on files that don't exist. Rather than take 5 seconds to rewrite Briar's code I instead
// The original code from Briar watches individual files, not a
// directory and Android's file observer
// won't work on files that don't exist. Rather than take 5 seconds to
// rewrite Briar's code I instead
// just make sure the file exists
if (cookieFile.exists() == false && cookieFile.createNewFile() == false) {
throw new RuntimeException("Could not create cookieFile");
@ -379,18 +440,25 @@ public abstract class OnionProxyManager {
onionProxyContext.setEnvironmentArgsAndWorkingDirectoryForStart(processBuilder);
Process torProcess = null;
try {
// torProcess = Runtime.getRuntime().exec(cmd, env, workingDirectory);
// torProcess = Runtime.getRuntime().exec(cmd, env,
// workingDirectory);
torProcess = processBuilder.start();
CountDownLatch controlPortCountDownLatch = new CountDownLatch(1);
eatStream(torProcess.getInputStream(), false, controlPortCountDownLatch);
eatStream(torProcess.getErrorStream(), true, null);
// On platforms other than Windows we run as a daemon and so we need to wait for the process to detach
// or exit. In the case of Windows the equivalent is running as a service and unfortunately that requires
// managing the service, such as turning it off or uninstalling it when it's time to move on. Any number
// of errors can prevent us from doing the cleanup and so we would leave the process running around. Rather
// than do that on Windows we just let the process run on the exec and hence don't look for an exit code.
// This does create a condition where the process has exited due to a problem but we should hopefully
// On platforms other than Windows we run as a daemon and so we need
// to wait for the process to detach
// or exit. In the case of Windows the equivalent is running as a
// service and unfortunately that requires
// managing the service, such as turning it off or uninstalling it
// when it's time to move on. Any number
// of errors can prevent us from doing the cleanup and so we would
// leave the process running around. Rather
// than do that on Windows we just let the process run on the exec
// and hence don't look for an exit code.
// This does create a condition where the process has exited due to
// a problem but we should hopefully
// detect that when we try to use the control connection.
if (OsData.getOsType() != OsData.OsType.Windows) {
int exit = torProcess.waitFor();
@ -418,11 +486,12 @@ public abstract class OnionProxyManager {
// Tell Tor to exit when the control connection is closed
controlConnection.takeOwnership();
controlConnection.resetConf(Collections.singletonList(OWNER));
// Register to receive events from the Tor process
controlConnection.setEventHandler(new OnionProxyManagerEventHandler());
controlConnection.setEventHandler(eventHandler);
controlConnection.setEvents(Arrays.asList(EVENTS));
// We only set the class property once the connection is in a known good state
// We only set the class property once the connection is in a known
// good state
this.controlConnection = controlConnection;
return true;
} catch (SecurityException e) {
@ -434,8 +503,10 @@ public abstract class OnionProxyManager {
return false;
} finally {
if (controlConnection == null && torProcess != null) {
// It's possible that something 'bad' could happen after we executed exec but before we takeOwnership()
// in which case the Tor OP will hang out as a zombie until this process is killed. This is problematic
// It's possible that something 'bad' could happen after we
// executed exec but before we takeOwnership()
// in which case the Tor OP will hang out as a zombie until this
// process is killed. This is problematic
// when we want to do things like
torProcess.destroy();
}
@ -443,8 +514,8 @@ public abstract class OnionProxyManager {
}
/**
* Returns the root directory in which the Tor Onion Proxy keeps its files. This is mostly intended
* for debugging purposes.
* Returns the root directory in which the Tor Onion Proxy keeps its files.
* This is mostly intended for debugging purposes.
*
* @return Working directory for Tor Onion Proxy files
*/
@ -452,7 +523,8 @@ public abstract class OnionProxyManager {
return onionProxyContext.getWorkingDirectory();
}
protected void eatStream(final InputStream inputStream, final boolean stdError, final CountDownLatch countDownLatch) {
protected void eatStream(final InputStream inputStream, final boolean stdError,
final CountDownLatch countDownLatch) {
new Thread("eatStream") {
@Override
public void run() {
@ -464,13 +536,15 @@ public abstract class OnionProxyManager {
LOG.error(scanner.nextLine());
} else {
String nextLine = scanner.nextLine();
// We need to find the line where it tells us what the control port is.
// The line that will appear in stdio with the control port looks like:
// We need to find the line where it tells us what
// the control port is.
// The line that will appear in stdio with the
// control port looks like:
// Control listener listening on port 39717.
if (nextLine.contains("Control listener listening on port ")) {
// For the record, I hate regex so I'm doing this manually
control_port =
Integer.parseInt(
// For the record, I hate regex so I'm doing
// this manually
control_port = Integer.parseInt(
nextLine.substring(nextLine.lastIndexOf(" ") + 1, nextLine.length() - 1));
countDownLatch.countDown();
}
@ -497,15 +571,19 @@ public abstract class OnionProxyManager {
throw new RuntimeException("could not make Tor executable.");
}
// We need to edit the config file to specify exactly where the cookie/geoip files should be stored, on
// Android this is always a fixed location relative to the configFiles which is why this extra step
// wasn't needed in Briar's Android code. But in Windows it ends up in the user's AppData/Roaming. Rather
// We need to edit the config file to specify exactly where the
// cookie/geoip files should be stored, on
// Android this is always a fixed location relative to the configFiles
// which is why this extra step
// wasn't needed in Briar's Android code. But in Windows it ends up in
// the user's AppData/Roaming. Rather
// than track it down we just tell Tor where to put it.
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(new BufferedWriter(new FileWriter(onionProxyContext.getTorrcFile(), true)));
printWriter.println("CookieAuthFile " + onionProxyContext.getCookieFile().getAbsolutePath());
// For some reason the GeoIP's location can only be given as a file name, not a path and it has
// For some reason the GeoIP's location can only be given as a file
// name, not a path and it has
// to be in the data directory so we need to set both
printWriter.println("DataDirectory " + onionProxyContext.getWorkingDirectory().getAbsolutePath());
printWriter.println("GeoIPFile " + onionProxyContext.getGeoIpFile().getName());
@ -520,7 +598,8 @@ public abstract class OnionProxyManager {
/**
* Alas old versions of Android do not support setExecutable.
*
* @param f File to make executable
* @param f
* File to make executable
* @return True if it worked, otherwise false.
*/
protected abstract boolean setExecutable(File f);

View file

@ -29,6 +29,8 @@ limitations under the License.
package com.msopentech.thali.toronionproxy;
import io.nucleo.net.HiddenServiceDescriptor;
import io.nucleo.net.HiddenServiceReadyListener;
import net.freehaven.tor.control.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,26 +44,43 @@ import java.util.List;
*/
public class OnionProxyManagerEventHandler implements EventHandler {
private static final Logger LOG = LoggerFactory.getLogger(OnionProxyManagerEventHandler.class);
private HiddenServiceDescriptor hs;
private HiddenServiceReadyListener listener;
private boolean hsPublished;
public void setHStoWatchFor(HiddenServiceDescriptor hs, HiddenServiceReadyListener listener) {
if (hs == this.hs && hsPublished) {
listener.onConnect(hs);
return;
}
this.listener = listener;
this.hs = hs;
hsPublished = false;
}
@Override
public void circuitStatus(String status, String id, String path) {
String msg = "CircuitStatus: " + id + " " + status + ", " + path;
LOG.info(msg);
LOG.debug(msg);
}
@Override
public void streamStatus(String status, String id, String target) {
LOG.info("streamStatus: status: " + status + ", id: " + id + ", target: " + target);
final String msg = "streamStatus: status: " + status + ", id: " + id + ", target: " + target;
LOG.debug(msg);
}
@Override
public void orConnStatus(String status, String orName) {
LOG.info("OR connection: status: " + status + ", orName: " + orName);
final String msg = "OR connection: status: " + status + ", orName: " + orName;
LOG.debug(msg);
}
@Override
public void bandwidthUsed(long read, long written) {
LOG.info("bandwidthUsed: read: " + read + ", written: " + written);
LOG.debug("bandwidthUsed: read: " + read + ", written: " + written);
}
@Override
@ -71,17 +90,33 @@ public class OnionProxyManagerEventHandler implements EventHandler {
while (iterator.hasNext()) {
stringBuilder.append(iterator.next());
}
LOG.info("newDescriptors: " + stringBuilder.toString());
final String msg = "newDescriptors: " + stringBuilder.toString();
LOG.debug(msg);
}
@Override
public void message(String severity, String msg) {
LOG.info("message: severity: " + severity + ", msg: " + msg);
final String msg2 = "message: severity: " + severity + ", msg: " + msg;
LOG.debug(msg2);
if (severity.equalsIgnoreCase("INFO"))
checkforHS(msg);
}
@Override
public void unrecognized(String type, String msg) {
LOG.info("unrecognized: type: " + type + ", msg: " + msg);
final String msg2 = "unrecognized: type: " + type + ", msg: " + msg;
LOG.debug(msg2);
}
private void checkforHS(String msg) {
if (hs == null || hsPublished == true)
return;
String pattern = "uploading rendezvous descriptor";
if (msg.toLowerCase().contains(pattern)) {
hsPublished = true;
LOG.info("Hidden service " + hs.getFullAddress() + " published.");
listener.onConnect(hs);
}
}
}

View file

@ -34,7 +34,6 @@ import java.util.Scanner;
public class OsData {
public enum OsType {Windows, Linux32, Linux64, Mac, Android}
private static OsType detectedType = null;
public static OsType getOsType() {
@ -87,7 +86,7 @@ public class OsData {
throw new RuntimeException("Uname returned error code " + exit);
}
if (unameOutput.compareTo("i686") == 0) {
if (unameOutput.matches("i.86")) {
return OsType.Linux32;
}
if (unameOutput.compareTo("x86_64") == 0) {

View file

@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit;
public interface WriteObserver {
/**
* Waits timeout of unit to see if file is modified
*
* @param timeout How long to wait before returning
* @param unit Unit to wait in
* @return True if file was modified, false if it was not

View file

@ -86,7 +86,7 @@ public abstract class Connection implements Closeable {
}
protected void onMessage(Message msg) throws IOException {
log.debug("onMessage: " + msg.toString());
log.debug("RXD: " + msg.toString());
if (msg instanceof ContainerMessage) {
synchronized (connectionListeners) {
for (ConnectionListener l : connectionListeners)

View file

@ -1,7 +1,6 @@
package io.nucleo.net;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class HiddenServiceDescriptor extends ServiceDescriptor {
@ -11,7 +10,7 @@ public class HiddenServiceDescriptor extends ServiceDescriptor {
public HiddenServiceDescriptor(String serviceName, int localPort, int servicePort) throws IOException {
super(serviceName, servicePort);
this.localPort = localPort;
this.serverSocket.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), localPort));
this.serverSocket.bind(new InetSocketAddress(TorNode.PROXY_LOCALHOST, localPort));
}
public int getLocalPort() {

View file

@ -0,0 +1,7 @@
package io.nucleo.net;
public interface HiddenServiceReadyListener {
public void onConnect(HiddenServiceDescriptor descriptor);
}

View file

@ -0,0 +1,15 @@
package io.nucleo.net;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import java.io.File;
import java.io.IOException;
public class JavaTorNode extends TorNode<JavaOnionProxyManager, JavaOnionProxyContext> {
public JavaTorNode(File torDirectory) throws IOException {
super(new JavaOnionProxyManager(new JavaOnionProxyContext(torDirectory)));
}
}

View file

@ -9,6 +9,7 @@ import io.nucleo.net.proto.exceptions.ProtocolViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@ -220,7 +221,6 @@ public class Node {
try {
socket.setSoTimeout(60 * 1000);
} catch (SocketException e2) {
e2.printStackTrace();
try {
socket.close();
@ -236,6 +236,8 @@ public class Node {
try {
out = prepareOOSForSocket(socket);
objectInputStream = new ObjectInputStream(socket.getInputStream());
} catch (EOFException e) {
log.info("Got bogus incoming connection");
} catch (IOException e) {
e.printStackTrace();
try {

View file

@ -7,39 +7,28 @@ import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.GregorianCalendar;
public abstract class TorNode<M extends OnionProxyManager, C extends OnionProxyContext> {
private static final String PROXY_LOCALHOST = "127.0.0.1";
static final String PROXY_LOCALHOST = "127.0.0.1";
private static final int RETRY_SLEEP = 500;
private static final int TOTAL_SEC_PER_STARTUP = 4 * 60;
private static final int TRIES_PER_STARTUP = 5;
private static final int TRIES_PER_HS_STARTUP = 150;
private static final Logger log = LoggerFactory.getLogger(TorNode.class);
private final OnionProxyManager tor;
private final Socks5Proxy proxy;
@SuppressWarnings("unchecked")
public TorNode(File torDirectory) throws IOException, InstantiationException {
Class<M> mgr;
Class<C> ctx;
try {
mgr = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
ctx = (Class<C>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
} catch (Throwable t) {
throw new InstantiationException(
"Could not reify Types of OnionProxyManager and OnionProxyContext! Is this class being used with raw types?");
}
log.debug("Running Tornode with " + mgr.getSimpleName() + " and " + ctx.getSimpleName());
tor = initTor(torDirectory, mgr, ctx);
public TorNode(M mgr) throws IOException {
OnionProxyContext ctx = mgr.getOnionProxyContext();
log.debug("Running Tornode with " + mgr.getClass().getSimpleName() + " and " + ctx.getClass().getSimpleName());
tor = initTor(mgr, ctx);
int proxyPort = tor.getIPv4LocalHostSocksPort();
log.info("TorSocks running on port " + proxyPort);
this.proxy = setupSocksProxy(proxyPort);
@ -85,78 +74,49 @@ public abstract class TorNode<M extends OnionProxyManager, C extends OnionProxyC
throw new IOException("Cannot connect to hidden service");
}
public HiddenServiceDescriptor createHiddenService(int localPort, int servicePort) throws IOException {
long before = GregorianCalendar.getInstance().getTimeInMillis();
String hiddenServiceName = tor.publishHiddenService(servicePort, localPort);
public void addHiddenServiceReadyListener(HiddenServiceDescriptor hiddenServiceDescriptor,
HiddenServiceReadyListener listener) throws IOException {
tor.attachHiddenServiceReadyListener(hiddenServiceDescriptor, listener);
}
public HiddenServiceDescriptor createHiddenService(final int localPort, final int servicePort) throws IOException {
return createHiddenService(localPort, servicePort, null);
}
public HiddenServiceDescriptor createHiddenService(final int localPort, final int servicePort,
final HiddenServiceReadyListener listener) throws IOException {
log.info("Publishing Hidden Service. This will at least take half a minute...");
final String hiddenServiceName = tor.publishHiddenService(servicePort, localPort);
final HiddenServiceDescriptor hiddenServiceDescriptor = new HiddenServiceDescriptor(hiddenServiceName,
localPort, servicePort);
return tryConnectToHiddenService(servicePort, before, hiddenServiceName, hiddenServiceDescriptor);
}
private HiddenServiceDescriptor tryConnectToHiddenService(int servicePort, long before, String hiddenServiceName,
final HiddenServiceDescriptor hiddenServiceDescriptor) throws IOException {
new Thread(new Runnable() {
@Override
public void run() {
try {
hiddenServiceDescriptor.getServerSocket().accept().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 0; i < TRIES_PER_HS_STARTUP; ++i) {
try {
final Socket socket = connectToHiddenService(hiddenServiceName, servicePort, 1, false);
socket.close();
} catch (IOException e) {
log.info("Hidden service " + hiddenServiceName + ":" + servicePort + " is not yet reachable");
try {
Thread.sleep(RETRY_SLEEP);
} catch (InterruptedException e1) {
}
continue;
}
log.info("Took " + (GregorianCalendar.getInstance().getTimeInMillis() - before)
+ " milliseconds to connect to publish " + hiddenServiceName + ":" + servicePort);
if (listener != null)
tor.attachHiddenServiceReadyListener(hiddenServiceDescriptor, listener);
return hiddenServiceDescriptor;
}
throw new IOException("Could not publish Hidden Service!");
}
public HiddenServiceDescriptor createHiddenService(int port) throws IOException {
return createHiddenService(port, port);
public HiddenServiceDescriptor createHiddenService(int port, HiddenServiceReadyListener listener)
throws IOException {
return createHiddenService(port, port, listener);
}
public void shutdown() throws IOException {
tor.stop();
}
static <M extends OnionProxyManager, C extends OnionProxyContext> OnionProxyManager initTor(File torDir,
Class<M> mgrType, Class<C> ctxType) throws IOException {
static <M extends OnionProxyManager, C extends OnionProxyContext> OnionProxyManager initTor(final M mgr, C ctx)
throws IOException {
log.debug("Trying to start tor in directory {}", torDir);
C ctx;
final M onionProxyManager;
try {
ctx = ctxType.getConstructor(File.class).newInstance(torDir);
onionProxyManager = mgrType.getConstructor(OnionProxyContext.class).newInstance(ctx);
} catch (Exception e1) {
throw new IOException(e1);
}
log.debug("Trying to start tor in directory {}", mgr.getWorkingDirectory());
try {
if (!onionProxyManager.startWithRepeat(TOTAL_SEC_PER_STARTUP, TRIES_PER_STARTUP)) {
if (!mgr.startWithRepeat(TOTAL_SEC_PER_STARTUP, TRIES_PER_STARTUP)) {
throw new IOException("Could not Start Tor. Is another instance already running?");
} else {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
onionProxyManager.stop();
mgr.stop();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@ -165,6 +125,6 @@ public abstract class TorNode<M extends OnionProxyManager, C extends OnionProxyC
} catch (InterruptedException e) {
throw new IOException(e);
}
return onionProxyManager;
return mgr;
}
}

View file

@ -1,6 +1,7 @@
ControlPort auto
CookieAuthentication 1
DisableNetwork 1
AvoidDiskWrites 1
PidFile pid
RunAsDaemon 1
SafeSocks 1

View file

@ -13,18 +13,27 @@ import java.util.concurrent.ExecutionException;
public class TorNodeTest {
private static final int hsPort = 55555;
private static CountDownLatch serverLatch = new CountDownLatch(1);
private static CountDownLatch serverLatch = new CountDownLatch(2);
private static TorNode<JavaOnionProxyManager, JavaOnionProxyContext> node;
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException, InstantiationException {
public static void main(String[] args)
throws IOException, InterruptedException, ExecutionException, InstantiationException {
File dir = new File("tor-test");
dir.mkdirs();
for (String str : args)
System.out.print(str + " ");
node = new TorNode<JavaOnionProxyManager, JavaOnionProxyContext>(dir) {
};
final ServiceDescriptor hiddenService = node.createHiddenService(hsPort);
node = new JavaTorNode(dir);
final ServiceDescriptor hiddenService = node.createHiddenService(hsPort, new HiddenServiceReadyListener() {
@Override
public void onConnect(HiddenServiceDescriptor descriptor) {
System.out.println("Successfully published hidden service " + descriptor.getFullAddress());
serverLatch.countDown();
}
});
new Thread(new Server(hiddenService.getServerSocket())).start();
serverLatch.await();
@ -94,7 +103,8 @@ public class TorNodeTest {
while (true) {
Socket sock = socket.accept();
System.out.println("Accepting Client " + sock.getRemoteSocketAddress() + " on port " + sock.getLocalPort());
System.out.println(
"Accepting Client " + sock.getRemoteSocketAddress() + " on port " + sock.getLocalPort());
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
OutputStreamWriter out = new OutputStreamWriter(sock.getOutputStream());
String aLine = null;

View file

@ -61,11 +61,16 @@ public class NodeTest {
if (args.length == 2) {
File dir = new File(args[0]);
dir.mkdirs();
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> tor = new TorNode<JavaOnionProxyManager, JavaOnionProxyContext>(
dir) {
};
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> tor = new JavaTorNode(dir);
node = new Node(tor.createHiddenService(Integer.parseInt(args[1])), tor);
node = new Node(tor.createHiddenService(Integer.parseInt(args[1]), new HiddenServiceReadyListener() {
@Override
public void onConnect(HiddenServiceDescriptor descriptor) {
System.out.println("Successfully published hidden service " + descriptor.getFullAddress() + " ");
}
}), tor);
} else {
node = new Node(new TCPServiceDescriptor("localhost", Integer.parseInt(args[0])));
}
@ -138,7 +143,7 @@ public class NodeTest {
default:
break;
}
System.out.print("\n" + node.getLocalName() + ":" + (currentCon == null ? "" : currentCon.getPeer()) + " >");
System.out.print("\n" + node.getLocalName() + (currentCon == null ? "" : (":" + currentCon.getPeer())) + " >");
}
}

View file

@ -118,9 +118,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private void init(int networkId, File storageDir) {
Log.traceCall();
Address persisted = dbStorage.initAndGetPersisted("myOnionAddress");
if (persisted != null)
this.myOnionAddress = persisted;
Address persistedOnionAddress = dbStorage.initAndGetPersisted("myOnionAddress");
if (persistedOnionAddress != null)
this.myOnionAddress = persistedOnionAddress;
// network
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);

View file

@ -12,6 +12,7 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Utils;
import io.nucleo.net.HiddenServiceDescriptor;
import io.nucleo.net.JavaTorNode;
import io.nucleo.net.TorNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -84,8 +85,6 @@ public class TorNetworkNode extends NetworkNode {
startServer(hiddenServiceDescriptor.getServerSocket());
setupListeners.stream().forEach(e -> e.onHiddenServicePublished());
/* UserThread.runAfter(() -> setupListeners.stream().forEach(e -> e.onHiddenServicePublished()),
500, TimeUnit.MILLISECONDS);*/
});
});
}
@ -214,9 +213,7 @@ public class TorNetworkNode extends NetworkNode {
log.info("TorDir = " + torDir.getAbsolutePath());
log.trace("Create TorNode");
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode =
new TorNode<JavaOnionProxyManager, JavaOnionProxyContext>(torDir) {
};
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode = new JavaTorNode(torDir);
log.info("\n\n############################################################\n" +
"TorNode created:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
@ -245,28 +242,31 @@ public class TorNetworkNode extends NetworkNode {
private void createHiddenService(TorNode torNode, int localPort, int servicePort,
Consumer<HiddenServiceDescriptor> resultHandler) {
Log.traceCall();
ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> {
ListenableFuture<Object> future = executorService.submit(() -> {
Utilities.setThreadName("TorNetworkNode:CreateHiddenService");
try {
long ts = System.currentTimeMillis();
log.debug("Create hidden service");
HiddenServiceDescriptor hiddenServiceDescriptor = torNode.createHiddenService(localPort, servicePort);
torNode.addHiddenServiceReadyListener(hiddenServiceDescriptor, descriptor -> {
log.info("\n\n############################################################\n" +
"Hidden service created:" +
"\nAddress=" + hiddenServiceDescriptor.getFullAddress() +
"Hidden service published:" +
"\nAddress=" + descriptor.getFullAddress() +
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n");
return hiddenServiceDescriptor;
UserThread.execute(() -> resultHandler.accept(hiddenServiceDescriptor));
});
return null;
} catch (Throwable t) {
throw t;
}
});
Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() {
public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) {
UserThread.execute(() -> {
resultHandler.accept(hiddenServiceDescriptor);
});
Futures.addCallback(future, new FutureCallback<Object>() {
public void onSuccess(Object hiddenServiceDescriptor) {
log.debug("HiddenServiceDescriptor created. Wait for publishing.");
}
public void onFailure(@NotNull Throwable throwable) {

View file

@ -120,8 +120,8 @@ public class TestUtils {
seedNodesRepository.setTorSeedNodeAddresses(seedNodes);
}
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost, 2,
encryptionService, keyRing, new File("dummy"));
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost,
2, new File("dummy"), encryptionService, keyRing);
p2PService.start(new P2PServiceListener() {
@Override
public void onRequestingDataCompleted() {