Patched jtorctl so a control connection can be used by multiple threads.

Various thread safety fixes, saner use of exceptions, and code
reformatting. The Tor plugin now creates a single control connection at
startup and closes it at shutdown. Fixes issue #3611962.
This commit is contained in:
akwizgran
2013-05-16 20:02:48 +01:00
parent c3e9ada0d9
commit 12192aea43
7 changed files with 1718 additions and 47 deletions

View File

@@ -12,7 +12,7 @@
<classpathentry kind="lib" path="libs/bluecove-2.1.1-SNAPSHOT-briar.jar"/> <classpathentry kind="lib" path="libs/bluecove-2.1.1-SNAPSHOT-briar.jar"/>
<classpathentry kind="lib" path="libs/bluecove-gpl-2.1.1-SNAPSHOT.jar"/> <classpathentry kind="lib" path="libs/bluecove-gpl-2.1.1-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="libs/javax.inject.jar"/> <classpathentry kind="lib" path="libs/javax.inject.jar"/>
<classpathentry kind="lib" path="libs/jtorctl.jar" sourcepath="libs/source/jtorctl-source.jar"/> <classpathentry kind="lib" path="/home/someone/workspace/prototype/briar-core/libs/jtorctl-briar.jar" sourcepath="libs/source/jtorctl-briar-source.jar"/>
<classpathentry kind="lib" path="libs/jsocks.jar" sourcepath="libs/source/jsocks-source.jar"/> <classpathentry kind="lib" path="libs/jsocks.jar" sourcepath="libs/source/jsocks-source.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/briar-api"/> <classpathentry combineaccessrules="false" kind="src" path="/briar-api"/>
<classpathentry kind="lib" path="/briar-api/libs/android.jar"/> <classpathentry kind="lib" path="/briar-api/libs/android.jar"/>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -73,6 +73,8 @@ class TorPlugin implements DuplexPlugin, EventHandler {
private volatile Process tor = null; private volatile Process tor = null;
private volatile int pid = -1; private volatile int pid = -1;
private volatile ServerSocket socket = null; private volatile ServerSocket socket = null;
private volatile Socket controlSocket = null;
private volatile TorControlConnection controlConnection = null;
TorPlugin(Executor pluginExecutor, Context appContext, TorPlugin(Executor pluginExecutor, Context appContext,
ShutdownManager shutdownManager, DuplexPluginCallback callback, ShutdownManager shutdownManager, DuplexPluginCallback callback,
@@ -113,9 +115,8 @@ class TorPlugin implements DuplexPlugin, EventHandler {
return false; return false;
} }
// Try to connect to an existing Tor process if there is one // Try to connect to an existing Tor process if there is one
Socket s;
try { try {
s = new Socket("127.0.0.1", CONTROL_PORT); // FIXME: Never closed controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
if(LOG.isLoggable(INFO)) LOG.info("Tor is already running"); if(LOG.isLoggable(INFO)) LOG.info("Tor is already running");
} catch(IOException e) { } catch(IOException e) {
// Install the binary, GeoIP database and config file if necessary // Install the binary, GeoIP database and config file if necessary
@@ -168,7 +169,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
return false; return false;
} }
// Now we should be able to connect to the new process // Now we should be able to connect to the new process
s = new Socket("127.0.0.1", CONTROL_PORT); // FIXME: Never closed controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
} }
// Read the PID of the Tor process so we can kill it if necessary // Read the PID of the Tor process so we can kill it if necessary
try { try {
@@ -187,12 +188,11 @@ class TorPlugin implements DuplexPlugin, EventHandler {
} }
}); });
// Open a control connection and authenticate using the cookie file // Open a control connection and authenticate using the cookie file
TorControlConnection control = new TorControlConnection(s); controlConnection = new TorControlConnection(controlSocket);
control.launchThread(true); controlConnection.authenticate(read(cookieFile));
control.authenticate(read(cookieFile));
// Register to receive events from the Tor process // Register to receive events from the Tor process
control.setEventHandler(this); controlConnection.setEventHandler(this);
control.setEvents(Arrays.asList("NOTICE", "WARN", "ERR")); controlConnection.setEvents(Arrays.asList("NOTICE", "WARN", "ERR"));
running = true; running = true;
// Bind a server socket to receive incoming hidden service connections // Bind a server socket to receive incoming hidden service connections
pluginExecutor.execute(new Runnable() { pluginExecutor.execute(new Runnable() {
@@ -306,6 +306,11 @@ class TorPlugin implements DuplexPlugin, EventHandler {
} }
} }
private void listFiles(File f) {
if(f.isDirectory()) for(File f1 : f.listFiles()) listFiles(f1);
else if(LOG.isLoggable(INFO)) LOG.info(f.getAbsolutePath());
}
private byte[] read(File f) throws IOException { private byte[] read(File f) throws IOException {
byte[] b = new byte[(int) f.length()]; byte[] b = new byte[(int) f.length()];
FileInputStream in = new FileInputStream(f); FileInputStream in = new FileInputStream(f);
@@ -376,17 +381,12 @@ class TorPlugin implements DuplexPlugin, EventHandler {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
FileObserver obs = new WriteObserver(hostnameFile, latch); FileObserver obs = new WriteObserver(hostnameFile, latch);
obs.startWatching(); obs.startWatching();
// Open a control connection and update the Tor config // Use the control connection to update the Tor config
List<String> config = Arrays.asList( List<String> config = Arrays.asList(
"HiddenServiceDir " + torDirectory.getAbsolutePath(), "HiddenServiceDir " + torDirectory.getAbsolutePath(),
"HiddenServicePort 80 127.0.0.1:" + port); "HiddenServicePort 80 127.0.0.1:" + port);
// FIXME: Socket isn't closed controlConnection.setConf(config);
Socket s = new Socket("127.0.0.1", CONTROL_PORT); controlConnection.saveConf();
TorControlConnection control = new TorControlConnection(s);
control.launchThread(true);
control.authenticate(read(cookieFile));
control.setConf(config);
control.saveConf();
// Wait for the hostname file to be created/updated // Wait for the hostname file to be created/updated
if(!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) { if(!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) {
if(LOG.isLoggable(WARNING)) if(LOG.isLoggable(WARNING))
@@ -437,12 +437,14 @@ class TorPlugin implements DuplexPlugin, EventHandler {
if(socket != null) tryToClose(socket); if(socket != null) tryToClose(socket);
try { try {
if(LOG.isLoggable(INFO)) LOG.info("Stopping Tor"); if(LOG.isLoggable(INFO)) LOG.info("Stopping Tor");
// FIXME: Socket isn't closed if(controlSocket == null)
Socket s = new Socket("127.0.0.1", CONTROL_PORT); controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
TorControlConnection control = new TorControlConnection(s); if(controlConnection == null) {
control.launchThread(true); controlConnection = new TorControlConnection(controlSocket);
control.authenticate(read(cookieFile)); controlConnection.authenticate(read(cookieFile));
control.shutdownTor("TERM"); }
controlConnection.shutdownTor("TERM");
controlSocket.close();
} catch(IOException e) { } catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if(LOG.isLoggable(INFO)) LOG.info("Killing Tor"); if(LOG.isLoggable(INFO)) LOG.info("Killing Tor");
@@ -515,38 +517,21 @@ class TorPlugin implements DuplexPlugin, EventHandler {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
private void listFiles(File f) { public void circuitStatus(String status, String circID, String path) {}
if(f.isDirectory()) for(File f1 : f.listFiles()) listFiles(f1);
else if(LOG.isLoggable(INFO)) LOG.info(f.getAbsolutePath());
}
public void circuitStatus(String status, String circID, String path) { public void streamStatus(String status, String streamID, String target) {}
if(LOG.isLoggable(INFO)) LOG.info("Circuit status");
}
public void streamStatus(String status, String streamID, String target) { public void orConnStatus(String status, String orName) {}
if(LOG.isLoggable(INFO)) LOG.info("Stream status");
}
public void orConnStatus(String status, String orName) { public void bandwidthUsed(long read, long written) {}
if(LOG.isLoggable(INFO)) LOG.info("OR connection status");
}
public void bandwidthUsed(long read, long written) { public void newDescriptors(List<String> orList) {}
if(LOG.isLoggable(INFO)) LOG.info("Bandwidth used");
}
public void newDescriptors(List<String> orList) {
if(LOG.isLoggable(INFO)) LOG.info("New descriptors");
}
public void message(String severity, String msg) { public void message(String severity, String msg) {
if(LOG.isLoggable(INFO)) LOG.info("Message: " + severity + " " + msg); if(LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
} }
public void unrecognized(String type, String msg) { public void unrecognized(String type, String msg) {}
if(LOG.isLoggable(INFO)) LOG.info("Unrecognized");
}
private static class WriteObserver extends FileObserver { private static class WriteObserver extends FileObserver {

1686
jtorctl.patch Normal file

File diff suppressed because it is too large Load Diff