mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Merge branch 'socks-socket' into 'master'
Fetch RSS feeds via Tor This patch replaces jsocks with our own minimal SOCKS 5 implementation, which is compatible with Android's OpenSSL hacks (see discussion on #599 for the horrifying details). This allows us to use OkHttp over Tor to fetch RSS feeds. It turns out that SOCKS 5 without authentication is a really simple protocol: https://tools.ietf.org/html/rfc1928 Closes #599. See merge request !308
This commit is contained in:
@@ -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") {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -9,4 +9,5 @@ public interface TorConstants {
|
||||
int SOCKS_PORT = 59050;
|
||||
int CONTROL_PORT = 59051;
|
||||
|
||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
22
briar-core/src/org/briarproject/socks/SocksModule.java
Normal file
22
briar-core/src/org/briarproject/socks/SocksModule.java
Normal 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);
|
||||
}
|
||||
}
|
||||
108
briar-core/src/org/briarproject/socks/SocksSocket.java
Normal file
108
briar-core/src/org/briarproject/socks/SocksSocket.java
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user