Use our own SOCKS implementation to talk to Tor.

This commit is contained in:
akwizgran
2016-09-03 00:00:08 +01:00
parent 387e44d114
commit 74f9a0dd73
15 changed files with 289 additions and 62 deletions

View File

@@ -22,6 +22,7 @@ import org.briarproject.reliability.ReliabilityModule;
import org.briarproject.reporting.ReportingModule;
import org.briarproject.settings.SettingsModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.socks.SocksModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
@@ -50,6 +51,7 @@ import dagger.Module;
ReportingModule.class,
SettingsModule.class,
SharingModule.class,
SocksModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,

View File

@@ -40,9 +40,6 @@ import org.briarproject.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
@@ -55,11 +52,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.net.SocketFactory;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
@@ -67,7 +66,6 @@ import static org.briarproject.api.feed.FeedConstants.FETCH_DELAY_INITIAL;
import static org.briarproject.api.feed.FeedConstants.FETCH_INTERVAL;
import static org.briarproject.api.feed.FeedConstants.FETCH_UNIT;
import static org.briarproject.api.feed.FeedConstants.KEY_FEEDS;
import static org.briarproject.api.plugins.TorConstants.SOCKS_PORT;
class FeedManagerImpl implements FeedManager, Client, EventListener {
@@ -79,6 +77,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
"466565644d616e6167657202fb797097"
+ "255af837abbf8c16e250b3c2ccc286eb"));
private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds
private final ScheduledExecutorService feedExecutor;
private final Executor ioExecutor;
private final DatabaseComponent db;
@@ -86,6 +86,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final BlogManager blogManager;
private final SocketFactory torSocketFactory;
private final AtomicBoolean fetcherStarted = new AtomicBoolean(false);
@Inject
@@ -99,7 +100,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
FeedManagerImpl(ScheduledExecutorService feedExecutor,
@IoExecutor Executor ioExecutor, DatabaseComponent db,
PrivateGroupFactory privateGroupFactory, ClientHelper clientHelper,
IdentityManager identityManager, BlogManager blogManager) {
IdentityManager identityManager, BlogManager blogManager,
SocketFactory torSocketFactory) {
this.feedExecutor = feedExecutor;
this.ioExecutor = ioExecutor;
@@ -108,6 +110,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.blogManager = blogManager;
this.torSocketFactory = torSocketFactory;
}
@Override
@@ -354,14 +357,10 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
}
private InputStream getFeedInputStream(String url) throws IOException {
// Set proxy
SocketAddress socketAddress =
new InetSocketAddress("127.0.0.1", SOCKS_PORT);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socketAddress);
// Build HTTP Client
OkHttpClient client = new OkHttpClient.Builder()
// .proxy(proxy)
.socketFactory(torSocketFactory)
.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
.build();
// Build Request

View File

@@ -1,9 +1,5 @@
package org.briarproject.reporting;
import net.sourceforge.jsocks.socks.Socks5Proxy;
import net.sourceforge.jsocks.socks.SocksException;
import net.sourceforge.jsocks.socks.SocksSocket;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.reporting.DevConfig;
import org.briarproject.api.reporting.DevReporter;
@@ -21,10 +17,10 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Logger;
import javax.net.SocketFactory;
import static java.util.logging.Level.WARNING;
class DevReporterImpl implements DevReporter {
@@ -35,21 +31,28 @@ class DevReporterImpl implements DevReporter {
private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds
private static final int LINE_LENGTH = 70;
private CryptoComponent crypto;
private DevConfig devConfig;
private final CryptoComponent crypto;
private final DevConfig devConfig;
private final SocketFactory torSocketFactory;
public DevReporterImpl(CryptoComponent crypto, DevConfig devConfig) {
public DevReporterImpl(CryptoComponent crypto, DevConfig devConfig,
SocketFactory torSocketFactory) {
this.crypto = crypto;
this.devConfig = devConfig;
this.torSocketFactory = torSocketFactory;
}
private Socket connectToDevelopers(int socksPort)
throws UnknownHostException, SocksException, SocketException {
Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", socksPort);
proxy.resolveAddrLocally(false);
Socket s = new SocksSocket(proxy, devConfig.getDevOnionAddress(), 80);
s.setSoTimeout(SOCKET_TIMEOUT);
return s;
private Socket connectToDevelopers() throws IOException {
String onion = devConfig.getDevOnionAddress();
Socket s = null;
try {
s = torSocketFactory.createSocket(onion, 80);
s.setSoTimeout(SOCKET_TIMEOUT);
return s;
} catch (IOException e) {
tryToClose(s);
throw e;
}
}
@Override
@@ -74,7 +77,7 @@ class DevReporterImpl implements DevReporter {
}
@Override
public void sendReports(File reportDir, int socksPort) {
public void sendReports(File reportDir) {
File[] reports = reportDir.listFiles();
if (reports == null || reports.length == 0)
return; // No reports to send
@@ -84,7 +87,7 @@ class DevReporterImpl implements DevReporter {
OutputStream out = null;
InputStream in = null;
try {
Socket s = connectToDevelopers(socksPort);
Socket s = connectToDevelopers();
out = s.getOutputStream();
in = new FileInputStream(f);
IoUtils.copy(in, out);
@@ -106,4 +109,12 @@ class DevReporterImpl implements DevReporter {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private void tryToClose(Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -4,6 +4,8 @@ import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.reporting.DevConfig;
import org.briarproject.api.reporting.DevReporter;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
@@ -12,7 +14,7 @@ public class ReportingModule {
@Provides
DevReporter provideDevReportTask(CryptoComponent crypto,
DevConfig devConfig) {
return new DevReporterImpl(crypto, devConfig);
DevConfig devConfig, SocketFactory torSocketFactory) {
return new DevReporterImpl(crypto, devConfig, torSocketFactory);
}
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.socks;
import java.net.InetSocketAddress;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.api.plugins.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
import static org.briarproject.api.plugins.TorConstants.SOCKS_PORT;
@Module
public class SocksModule {
@Provides
SocketFactory provideTorSocketFactory() {
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
SOCKS_PORT);
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT);
}
}

View File

@@ -0,0 +1,108 @@
package org.briarproject.socks;
import org.briarproject.util.ByteUtils;
import org.briarproject.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
class SocksSocket extends Socket {
private final SocketAddress proxy;
private final int connectToProxyTimeout;
SocksSocket(SocketAddress proxy, int connectToProxyTimeout) {
this.proxy = proxy;
this.connectToProxyTimeout = connectToProxyTimeout;
}
@Override
public void connect(SocketAddress endpoint, int timeout)
throws IOException {
// Validate the endpoint
if (!(endpoint instanceof InetSocketAddress))
throw new IllegalArgumentException();
InetSocketAddress inet = (InetSocketAddress) endpoint;
String host = inet.getHostName();
if (host.length() > 255) throw new IllegalArgumentException();
int port = inet.getPort();
// Connect to the proxy
super.connect(proxy, connectToProxyTimeout);
OutputStream out = getOutputStream();
InputStream in = getInputStream();
// Request SOCKS 5 with no authentication
sendMethodRequest(out);
receiveMethodResponse(in);
// Use the supplied timeout temporarily
int oldTimeout = getSoTimeout();
setSoTimeout(timeout);
// Connect to the endpoint via the proxy
sendConnectRequest(out, host, port);
receiveConnectResponse(in);
// Restore the old timeout
setSoTimeout(oldTimeout);
}
private void sendMethodRequest(OutputStream out) throws IOException {
byte[] methodRequest = new byte[] {
5, // SOCKS version is 5
1, // Number of methods is 1
0 // Method is 0, no authentication
};
out.write(methodRequest);
out.flush();
}
private void receiveMethodResponse(InputStream in) throws IOException {
byte[] methodResponse = new byte[2];
IoUtils.read(in, methodResponse);
byte version = methodResponse[0];
byte method = methodResponse[1];
if (version != 5)
throw new IOException("Unsupported SOCKS version: " + version);
if (method == (byte) 255)
throw new IOException("Proxy requires authentication");
if (method != 0)
throw new IOException("Unsupported auth method: " + method);
}
private void sendConnectRequest(OutputStream out, String host, int port)
throws IOException {
byte[] connectRequest = new byte[7 + host.length()];
connectRequest[0] = 5; // SOCKS version is 5
connectRequest[1] = 1; // Command is 1, connect
connectRequest[3] = 3; // Address type is 3, domain name
connectRequest[4] = (byte) host.length(); // Length of domain name
for (int i = 0; i < host.length(); i++)
connectRequest[5 + i] = (byte) host.charAt(i);
ByteUtils.writeUint16(port, connectRequest, connectRequest.length - 2);
out.write(connectRequest);
out.flush();
}
private void receiveConnectResponse(InputStream in) throws IOException {
byte[] connectResponse = new byte[4];
IoUtils.read(in, connectResponse);
byte version = connectResponse[0];
byte reply = connectResponse[1];
byte addressType = connectResponse[3];
if (version != 5)
throw new IOException("Unsupported SOCKS version: " + version);
if (reply != 0)
throw new IOException("Connection failed: " + reply);
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
else throw new IOException("Unsupported address type: " + addressType);
IoUtils.read(in, new byte[2]); // Port number
}
}

View File

@@ -0,0 +1,49 @@
package org.briarproject.socks;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import javax.net.SocketFactory;
class SocksSocketFactory extends SocketFactory {
private final SocketAddress proxy;
private final int connectToProxyTimeout;
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout) {
this.proxy = proxy;
this.connectToProxyTimeout = connectToProxyTimeout;
}
@Override
public Socket createSocket() {
return new SocksSocket(proxy, connectToProxyTimeout);
}
@Override
public Socket createSocket(String host, int port) throws IOException {
Socket socket = createSocket();
socket.connect(InetSocketAddress.createUnresolved(host, port));
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) {
throw new UnsupportedOperationException();
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost,
int localPort) {
throw new UnsupportedOperationException();
}
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,5 +1,7 @@
package org.briarproject.util;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -22,19 +24,34 @@ public class IoUtils {
throws IOException {
byte[] buf = new byte[4096];
try {
try {
while (true) {
int read = in.read(buf);
if (read == -1) break;
out.write(buf, 0, read);
}
out.flush();
} finally {
in.close();
while (true) {
int read = in.read(buf);
if (read == -1) break;
out.write(buf, 0, read);
}
} finally {
in.close();
out.flush();
out.close();
} catch (IOException e) {
tryToClose(in);
tryToClose(out);
}
}
private static void tryToClose(Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
// We did our best
}
}
public static void read(InputStream in, byte[] b) throws IOException {
int offset = 0;
while (offset < b.length) {
int read = in.read(b, offset, b.length - offset);
if (read == -1) throw new EOFException();
offset += read;
}
}
}