make Tor connections work with direct TLS

This commit is contained in:
Daniel Gultsch 2019-09-05 12:08:58 +02:00
parent 7ec1b443ab
commit 571c29f92a
3 changed files with 74 additions and 65 deletions

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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();
if (localSocket == null) {
throw new IOException("could not initialize ssl socket");
if (features.encryptionEnabled) {
localSocket = upgradeSocketToTls(localSocket);
}
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);
}
}
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
if (startXmpp(localSocket)) {
localSocket.setSoTimeout(0); //reset to 0; once the connection is established we dont 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,29 +792,8 @@ public class XmppConnection implements Runnable {
private void switchOverToTls() throws XmlPullParserException, IOException {
tagReader.readTag();
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);
}
final Socket socket = this.socket;
final SSLSocket sslSocket = upgradeSocketToTls(socket);
tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream());
sendStartStream();
@ -832,10 +807,26 @@ public class XmppConnection implements Runnable {
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");
}
private SSLSocket upgradeSocketToTls(final Socket socket) throws IOException {
final TlsFactoryVerifier tlsFactoryVerifier;
try {
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 {