make Tor connections work with direct TLS
This commit is contained in:
parent
7ec1b443ab
commit
571c29f92a
|
@ -76,12 +76,16 @@ public class Resolver {
|
|||
Result result = new Result();
|
||||
result.hostname = DNSName.from(hostname);
|
||||
result.port = port;
|
||||
result.directTls = port == 443 || port == 5223;
|
||||
result.directTls = useDirectTls(port);
|
||||
result.authenticated = true;
|
||||
return Collections.singletonList(result);
|
||||
}
|
||||
|
||||
|
||||
public static boolean useDirectTls(final int port) {
|
||||
return port == 443 || port == 5223;
|
||||
}
|
||||
|
||||
public static List<Result> resolve(String domain) {
|
||||
final List<Result> ipResults = fromIpAddress(domain);
|
||||
if (ipResults.size() > 0) {
|
||||
|
|
|
@ -21,7 +21,7 @@ public class SocksSocketFactory {
|
|||
byte[] response = new byte[2];
|
||||
proxyIs.read(response);
|
||||
if (response[0] != 0x05 || response[1] != 0x00) {
|
||||
throw new SocksConnectionException();
|
||||
throw new SocksConnectionException("Socks 5 handshake failed");
|
||||
}
|
||||
byte[] dest = destination.getBytes();
|
||||
ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
|
||||
|
@ -33,7 +33,13 @@ public class SocksSocketFactory {
|
|||
response = new byte[7 + dest.length];
|
||||
proxyIs.read(response);
|
||||
if (response[1] != 0x00) {
|
||||
throw new SocksConnectionException();
|
||||
if (response[1] == 0x04) {
|
||||
throw new HostNotFoundException("Host unreachable");
|
||||
}
|
||||
if (response[1] == 0x05) {
|
||||
throw new HostNotFoundException("Connection refused");
|
||||
}
|
||||
throw new SocksConnectionException("Unable to connect to destination "+(int) (response[1]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,11 +67,19 @@ public class SocksSocketFactory {
|
|||
return createSocket(new InetSocketAddress(InetAddress.getByAddress(LOCALHOST), 9050), destination, port);
|
||||
}
|
||||
|
||||
static class SocksConnectionException extends IOException {
|
||||
|
||||
private static class SocksConnectionException extends IOException {
|
||||
SocksConnectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SocksProxyNotFoundException extends IOException {
|
||||
|
||||
}
|
||||
|
||||
public static class HostNotFoundException extends SocksConnectionException {
|
||||
HostNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,8 +267,18 @@ public class XmppConnection implements Runnable {
|
|||
destination = account.getHostname();
|
||||
this.verifiedHostname = destination;
|
||||
}
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": connect to " + destination + " via Tor");
|
||||
localSocket = SocksSocketFactory.createSocketOverTor(destination, account.getPort());
|
||||
|
||||
final int port = account.getPort();
|
||||
final boolean directTls = Resolver.useDirectTls(port);
|
||||
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": connect to " + destination + " via Tor. directTls="+directTls);
|
||||
localSocket = SocksSocketFactory.createSocketOverTor(destination, port);
|
||||
|
||||
if (directTls) {
|
||||
localSocket = upgradeSocketToTls(localSocket);
|
||||
features.encryptionEnabled = true;
|
||||
}
|
||||
|
||||
try {
|
||||
startXmpp(localSocket);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -328,29 +338,13 @@ public class XmppConnection implements Runnable {
|
|||
+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
|
||||
}
|
||||
|
||||
if (!features.encryptionEnabled) {
|
||||
localSocket = new Socket();
|
||||
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
} else {
|
||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||
localSocket = tlsFactoryVerifier.factory.createSocket();
|
||||
localSocket = new Socket();
|
||||
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
|
||||
if (localSocket == null) {
|
||||
throw new IOException("could not initialize ssl socket");
|
||||
}
|
||||
|
||||
SSLSocketHelper.setSecurity((SSLSocket) localSocket);
|
||||
SSLSocketHelper.setHostname((SSLSocket) localSocket, account.getServer());
|
||||
SSLSocketHelper.setApplicationProtocol((SSLSocket) localSocket, "xmpp-client");
|
||||
|
||||
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
|
||||
if (!tlsFactoryVerifier.verifier.verify(account.getServer(), verifiedHostname, ((SSLSocket) localSocket).getSession())) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
|
||||
FileBackend.close(localSocket);
|
||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||
}
|
||||
if (features.encryptionEnabled) {
|
||||
localSocket = upgradeSocketToTls(localSocket);
|
||||
}
|
||||
|
||||
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
|
||||
if (startXmpp(localSocket)) {
|
||||
localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this
|
||||
|
@ -384,6 +378,8 @@ public class XmppConnection implements Runnable {
|
|||
this.changeStatus(e.state);
|
||||
} catch (final UnknownHostException | ConnectException e) {
|
||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||
} catch (final SocksSocketFactory.HostNotFoundException e) {
|
||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
|
||||
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
|
||||
} catch (final IOException | XmlPullParserException e) {
|
||||
|
@ -796,46 +792,41 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
private void switchOverToTls() throws XmlPullParserException, IOException {
|
||||
tagReader.readTag();
|
||||
final Socket socket = this.socket;
|
||||
final SSLSocket sslSocket = upgradeSocketToTls(socket);
|
||||
tagReader.setInputStream(sslSocket.getInputStream());
|
||||
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
||||
sendStartStream();
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS connection established");
|
||||
features.encryptionEnabled = true;
|
||||
final Tag tag = tagReader.readTag();
|
||||
if (tag != null && tag.isStart("stream")) {
|
||||
SSLSocketHelper.log(account, sslSocket);
|
||||
processStream();
|
||||
} else {
|
||||
throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
|
||||
}
|
||||
sslSocket.close();
|
||||
}
|
||||
|
||||
private SSLSocket upgradeSocketToTls(final Socket socket) throws IOException {
|
||||
final TlsFactoryVerifier tlsFactoryVerifier;
|
||||
try {
|
||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||
final InetAddress address = socket == null ? null : socket.getInetAddress();
|
||||
|
||||
if (address == null) {
|
||||
throw new IOException("could not setup ssl");
|
||||
}
|
||||
|
||||
final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
|
||||
|
||||
|
||||
if (sslSocket == null) {
|
||||
throw new IOException("could not initialize ssl socket");
|
||||
}
|
||||
|
||||
SSLSocketHelper.setSecurity(sslSocket);
|
||||
SSLSocketHelper.setHostname(sslSocket, account.getServer());
|
||||
SSLSocketHelper.setApplicationProtocol(sslSocket, "xmpp-client");
|
||||
|
||||
if (!tlsFactoryVerifier.verifier.verify(account.getServer(), this.verifiedHostname, sslSocket.getSession())) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
|
||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||
}
|
||||
tagReader.setInputStream(sslSocket.getInputStream());
|
||||
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
||||
sendStartStream();
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS connection established");
|
||||
features.encryptionEnabled = true;
|
||||
final Tag tag = tagReader.readTag();
|
||||
if (tag != null && tag.isStart("stream")) {
|
||||
SSLSocketHelper.log(account, sslSocket);
|
||||
processStream();
|
||||
} else {
|
||||
throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
|
||||
}
|
||||
sslSocket.close();
|
||||
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
|
||||
tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||
}
|
||||
final InetAddress address = socket.getInetAddress();
|
||||
final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
|
||||
SSLSocketHelper.setSecurity(sslSocket);
|
||||
SSLSocketHelper.setHostname(sslSocket, account.getServer());
|
||||
SSLSocketHelper.setApplicationProtocol(sslSocket, "xmpp-client");
|
||||
if (!tlsFactoryVerifier.verifier.verify(account.getServer(), this.verifiedHostname, sslSocket.getSession())) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed");
|
||||
FileBackend.close(sslSocket);
|
||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
||||
}
|
||||
return sslSocket;
|
||||
}
|
||||
|
||||
private void processStreamFeatures(final Tag currentTag) throws XmlPullParserException, IOException {
|
||||
|
|
Loading…
Reference in a new issue