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 result = new Result();
|
||||||
result.hostname = DNSName.from(hostname);
|
result.hostname = DNSName.from(hostname);
|
||||||
result.port = port;
|
result.port = port;
|
||||||
result.directTls = port == 443 || port == 5223;
|
result.directTls = useDirectTls(port);
|
||||||
result.authenticated = true;
|
result.authenticated = true;
|
||||||
return Collections.singletonList(result);
|
return Collections.singletonList(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean useDirectTls(final int port) {
|
||||||
|
return port == 443 || port == 5223;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<Result> resolve(String domain) {
|
public static List<Result> resolve(String domain) {
|
||||||
final List<Result> ipResults = fromIpAddress(domain);
|
final List<Result> ipResults = fromIpAddress(domain);
|
||||||
if (ipResults.size() > 0) {
|
if (ipResults.size() > 0) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class SocksSocketFactory {
|
||||||
byte[] response = new byte[2];
|
byte[] response = new byte[2];
|
||||||
proxyIs.read(response);
|
proxyIs.read(response);
|
||||||
if (response[0] != 0x05 || response[1] != 0x00) {
|
if (response[0] != 0x05 || response[1] != 0x00) {
|
||||||
throw new SocksConnectionException();
|
throw new SocksConnectionException("Socks 5 handshake failed");
|
||||||
}
|
}
|
||||||
byte[] dest = destination.getBytes();
|
byte[] dest = destination.getBytes();
|
||||||
ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
|
ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
|
||||||
|
@ -33,7 +33,13 @@ public class SocksSocketFactory {
|
||||||
response = new byte[7 + dest.length];
|
response = new byte[7 + dest.length];
|
||||||
proxyIs.read(response);
|
proxyIs.read(response);
|
||||||
if (response[1] != 0x00) {
|
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);
|
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 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();
|
destination = account.getHostname();
|
||||||
this.verifiedHostname = destination;
|
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 {
|
try {
|
||||||
startXmpp(localSocket);
|
startXmpp(localSocket);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
@ -328,29 +338,13 @@ public class XmppConnection implements Runnable {
|
||||||
+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
|
+ result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!features.encryptionEnabled) {
|
localSocket = new Socket();
|
||||||
localSocket = new Socket();
|
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||||
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
|
||||||
} else {
|
|
||||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
|
||||||
localSocket = tlsFactoryVerifier.factory.createSocket();
|
|
||||||
|
|
||||||
if (localSocket == null) {
|
if (features.encryptionEnabled) {
|
||||||
throw new IOException("could not initialize ssl socket");
|
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);
|
localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000);
|
||||||
if (startXmpp(localSocket)) {
|
if (startXmpp(localSocket)) {
|
||||||
localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this
|
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);
|
this.changeStatus(e.state);
|
||||||
} catch (final UnknownHostException | ConnectException e) {
|
} catch (final UnknownHostException | ConnectException e) {
|
||||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||||
|
} catch (final SocksSocketFactory.HostNotFoundException e) {
|
||||||
|
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||||
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
|
} catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
|
||||||
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
|
this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
|
||||||
} catch (final IOException | XmlPullParserException e) {
|
} catch (final IOException | XmlPullParserException e) {
|
||||||
|
@ -796,46 +792,41 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private void switchOverToTls() throws XmlPullParserException, IOException {
|
private void switchOverToTls() throws XmlPullParserException, IOException {
|
||||||
tagReader.readTag();
|
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 {
|
try {
|
||||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||||
final InetAddress address = socket == null ? null : socket.getInetAddress();
|
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
||||||
|
|
||||||
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");
|
|
||||||
throw new StateChangingException(Account.State.TLS_ERROR);
|
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 {
|
private void processStreamFeatures(final Tag currentTag) throws XmlPullParserException, IOException {
|
||||||
|
|
Loading…
Reference in a new issue