diff --git a/briar-android/build.gradle b/briar-android/build.gradle index adea13909d84b5fb0cb8f69d979fdd6e4315149c..dc93b62fb5c70e59fb8a55513742493a91e9efc4 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -11,9 +11,6 @@ dependencies { compile project(':briar-api') compile project(':briar-core') compile fileTree(dir: 'libs', include: '*.jar') - // This shouldn't be necessary; per section 23.4.4 of the Gradle docs: - // "file dependencies are included in transitive project dependencies within the same build". - compile files('../briar-core/libs/jsocks.jar') compile "com.android.support:support-v4:$supportVersion" compile("com.android.support:appcompat-v7:$supportVersion") { diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java index ca899bc8aed4e8a67cc0c13e3619426cbedd4c2a..4d49a13ac71e204fd45678065439aaf636d4f84b 100644 --- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java +++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.Executor; +import javax.net.SocketFactory; + import dagger.Module; import dagger.Provides; @@ -30,15 +32,16 @@ public class AndroidPluginsModule { @Provides public PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, - AndroidExecutor androidExecutor, - SecureRandom random, BackoffFactory backoffFactory, Application app, - LocationUtils locationUtils, DevReporter reporter, + AndroidExecutor androidExecutor, SecureRandom random, + SocketFactory torSocketFactory, BackoffFactory backoffFactory, + Application app, LocationUtils locationUtils, DevReporter reporter, EventBus eventBus) { Context appContext = app.getApplicationContext(); DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, androidExecutor, appContext, random, backoffFactory); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, - locationUtils, reporter, eventBus, backoffFactory); + locationUtils, reporter, eventBus, torSocketFactory, + backoffFactory); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, backoffFactory, appContext); final Collection<DuplexPluginFactory> duplex = diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index 0fcf4a87e61bcafa900ee59931d29f93396e41ab..3347c2477ee47bdce74ad69ac7b005e39dce8044 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -14,8 +14,6 @@ import android.os.PowerManager; import net.freehaven.tor.control.EventHandler; import net.freehaven.tor.control.TorControlConnection; -import net.sourceforge.jsocks.socks.Socks5Proxy; -import net.sourceforge.jsocks.socks.SocksSocket; import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.TransportId; @@ -62,6 +60,8 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.zip.ZipInputStream; +import javax.net.SocketFactory; + import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Context.MODE_PRIVATE; import static android.content.Context.POWER_SERVICE; @@ -74,7 +74,6 @@ import static java.util.logging.Level.WARNING; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static org.briarproject.api.plugins.TorConstants.CONTROL_PORT; -import static org.briarproject.api.plugins.TorConstants.SOCKS_PORT; import static org.briarproject.util.PrivacyUtils.scrubOnion; class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @@ -93,6 +92,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final Context appContext; private final LocationUtils locationUtils; private final DevReporter reporter; + private final SocketFactory torSocketFactory; private final Backoff backoff; private final DuplexPluginCallback callback; private final String architecture; @@ -110,13 +110,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private volatile BroadcastReceiver networkStateReceiver = null; TorPlugin(Executor ioExecutor, Context appContext, - LocationUtils locationUtils, DevReporter reporter, Backoff backoff, + LocationUtils locationUtils, DevReporter reporter, + SocketFactory torSocketFactory, Backoff backoff, DuplexPluginCallback callback, String architecture, int maxLatency, int maxIdleTime) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; + this.torSocketFactory = torSocketFactory; this.backoff = backoff; this.callback = callback; this.architecture = architecture; @@ -295,6 +297,14 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + private void tryToClose(Socket s) { + try { + if (s != null) s.close(); + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + } + private void listFiles(File f) { if (f.isDirectory()) for (File child : f.listFiles()) listFiles(child); else LOG.info(f.getAbsolutePath()); @@ -320,8 +330,9 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ioExecutor.execute(new Runnable() { @Override public void run() { + // TODO: Trigger this with a TransportEnabledEvent File reportDir = AndroidUtils.getReportDir(appContext); - reporter.sendReports(reportDir, SOCKS_PORT); + reporter.sendReports(reportDir); } }); } @@ -516,21 +527,22 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion); return null; } + Socket s = null; try { if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + scrubOnion(onion)); controlConnection.forgetHiddenService(onion); - Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT); - proxy.resolveAddrLocally(false); - Socket s = new SocksSocket(proxy, onion + ".onion", 80); + s = torSocketFactory.createSocket(onion + ".onion", 80); s.setSoTimeout(socketTimeout); if (LOG.isLoggable(INFO)) LOG.info("Connected to " + scrubOnion(onion)); return new TorTransportConnection(this, s); } catch (IOException e) { - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Could not connect to " + scrubOnion(onion) + ": " + e.toString()); + } + tryToClose(s); return null; } } diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index ae1b9e856a6cbac77de5b85ecb4860d21db00ca6..c85405f676ad3afad530fabfcb2ef0cd1fcecd04 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -18,6 +18,8 @@ import org.briarproject.api.system.LocationUtils; import java.util.concurrent.Executor; import java.util.logging.Logger; +import javax.net.SocketFactory; + public class TorPluginFactory implements DuplexPluginFactory { private static final Logger LOG = @@ -34,16 +36,19 @@ public class TorPluginFactory implements DuplexPluginFactory { private final LocationUtils locationUtils; private final DevReporter reporter; private final EventBus eventBus; + private final SocketFactory torSocketFactory; private final BackoffFactory backoffFactory; public TorPluginFactory(Executor ioExecutor, Context appContext, LocationUtils locationUtils, DevReporter reporter, - EventBus eventBus, BackoffFactory backoffFactory) { + EventBus eventBus, SocketFactory torSocketFactory, + BackoffFactory backoffFactory) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; this.reporter = reporter; this.eventBus = eventBus; + this.torSocketFactory = torSocketFactory; this.backoffFactory = backoffFactory; } @@ -81,8 +86,8 @@ public class TorPluginFactory implements DuplexPluginFactory { Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE); TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils, - reporter, backoff, callback, architecture, MAX_LATENCY, - MAX_IDLE_TIME); + reporter, torSocketFactory, backoff, callback, architecture, + MAX_LATENCY, MAX_IDLE_TIME); eventBus.addListener(plugin); return plugin; } diff --git a/briar-api/src/org/briarproject/api/plugins/TorConstants.java b/briar-api/src/org/briarproject/api/plugins/TorConstants.java index 3eec84e08c7b71d53ea51ff831daaca4202757b1..f90e047bb00c1374ea0884775a60a280e1116c99 100644 --- a/briar-api/src/org/briarproject/api/plugins/TorConstants.java +++ b/briar-api/src/org/briarproject/api/plugins/TorConstants.java @@ -9,4 +9,5 @@ public interface TorConstants { int SOCKS_PORT = 59050; int CONTROL_PORT = 59051; + int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds } diff --git a/briar-api/src/org/briarproject/api/reporting/DevReporter.java b/briar-api/src/org/briarproject/api/reporting/DevReporter.java index 7c0f14aa010caa3d12135eb3199637b2171adbf8..dfcbb9c1a96ff7bfe08fb6f0b5a80655981e94ee 100644 --- a/briar-api/src/org/briarproject/api/reporting/DevReporter.java +++ b/briar-api/src/org/briarproject/api/reporting/DevReporter.java @@ -22,7 +22,6 @@ public interface DevReporter { * Send reports previously stored on-disk. * * @param reportDir the directory where reports are stored. - * @param socksPort the SOCKS port of a Tor client. */ - void sendReports(File reportDir, int socksPort); + void sendReports(File reportDir); } diff --git a/briar-core/libs/jsocks.jar b/briar-core/libs/jsocks.jar deleted file mode 100644 index 5b5ff17ef666ab56f4fd647d37c9798e9798c269..0000000000000000000000000000000000000000 Binary files a/briar-core/libs/jsocks.jar and /dev/null differ diff --git a/briar-core/src/org/briarproject/CoreModule.java b/briar-core/src/org/briarproject/CoreModule.java index d865d6e43a25eebcf6ffa6198794703ab584be23..dbfaeb256a9c4d7f3670afbccf8181e61066212e 100644 --- a/briar-core/src/org/briarproject/CoreModule.java +++ b/briar-core/src/org/briarproject/CoreModule.java @@ -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, diff --git a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java index 496c28403dae49e02578fe1fcb56c4657d7f85fe..d0baff13791bb1bfb7018f245aa95f7f43b077b2 100644 --- a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java +++ b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java @@ -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 diff --git a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java index bd7154068ca901e2f1491e2e44fd67fba65dc352..7d7415fb018dcd74e87a0930469bc0dbde296721 100644 --- a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java +++ b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java @@ -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); + } + } } diff --git a/briar-core/src/org/briarproject/reporting/ReportingModule.java b/briar-core/src/org/briarproject/reporting/ReportingModule.java index 7c88261de3faae5b3d535d2683e0de242b1c7446..333b3afca06339862198382907349b3488f21472 100644 --- a/briar-core/src/org/briarproject/reporting/ReportingModule.java +++ b/briar-core/src/org/briarproject/reporting/ReportingModule.java @@ -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); } } diff --git a/briar-core/src/org/briarproject/socks/SocksModule.java b/briar-core/src/org/briarproject/socks/SocksModule.java new file mode 100644 index 0000000000000000000000000000000000000000..eaaa3c4d6231cb52b47949e07d31ca52151bec15 --- /dev/null +++ b/briar-core/src/org/briarproject/socks/SocksModule.java @@ -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); + } +} diff --git a/briar-core/src/org/briarproject/socks/SocksSocket.java b/briar-core/src/org/briarproject/socks/SocksSocket.java new file mode 100644 index 0000000000000000000000000000000000000000..7c627daef947e0bd3207c3046a3e9b888ca146ad --- /dev/null +++ b/briar-core/src/org/briarproject/socks/SocksSocket.java @@ -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 + } +} diff --git a/briar-core/src/org/briarproject/socks/SocksSocketFactory.java b/briar-core/src/org/briarproject/socks/SocksSocketFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..dfcd973597429e955de2b21cfa50bbbbe22ad42c --- /dev/null +++ b/briar-core/src/org/briarproject/socks/SocksSocketFactory.java @@ -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(); + } +} diff --git a/briar-core/src/org/briarproject/util/IoUtils.java b/briar-core/src/org/briarproject/util/IoUtils.java index c149b024e16017e51d91f449e0613f1f57b34815..494c3a854be4c68386b6ed080e84eec4df051428 100644 --- a/briar-core/src/org/briarproject/util/IoUtils.java +++ b/briar-core/src/org/briarproject/util/IoUtils.java @@ -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; + } + } }