unified all account state exceptions

This commit is contained in:
Daniel Gultsch 2017-05-05 09:33:05 +02:00
parent 404cf808b0
commit 7b6d49f329
4 changed files with 49 additions and 69 deletions

View File

@ -118,9 +118,11 @@ public class Account extends AbstractEntity {
REGISTRATION_CONFLICT(true), REGISTRATION_CONFLICT(true),
REGISTRATION_SUCCESSFUL, REGISTRATION_SUCCESSFUL,
REGISTRATION_NOT_SUPPORTED(true), REGISTRATION_NOT_SUPPORTED(true),
SECURITY_ERROR(true), TLS_ERROR(true),
INCOMPATIBLE_SERVER(true), INCOMPATIBLE_SERVER(true),
TOR_NOT_AVAILABLE(true), TOR_NOT_AVAILABLE(true),
DOWNGRADE_ATTACK(true),
SESSION_FAILURE(true),
BIND_FAILURE(true), BIND_FAILURE(true),
HOST_UNKNOWN(true), HOST_UNKNOWN(true),
REGISTRATION_PLEASE_WAIT(true), REGISTRATION_PLEASE_WAIT(true),
@ -168,14 +170,18 @@ public class Account extends AbstractEntity {
return R.string.account_status_regis_success; return R.string.account_status_regis_success;
case REGISTRATION_NOT_SUPPORTED: case REGISTRATION_NOT_SUPPORTED:
return R.string.account_status_regis_not_sup; return R.string.account_status_regis_not_sup;
case SECURITY_ERROR: case TLS_ERROR:
return R.string.account_status_security_error; return R.string.account_status_tls_error;
case INCOMPATIBLE_SERVER: case INCOMPATIBLE_SERVER:
return R.string.account_status_incompatible_server; return R.string.account_status_incompatible_server;
case TOR_NOT_AVAILABLE: case TOR_NOT_AVAILABLE:
return R.string.account_status_tor_unavailable; return R.string.account_status_tor_unavailable;
case BIND_FAILURE: case BIND_FAILURE:
return R.string.account_status_bind_failure; return R.string.account_status_bind_failure;
case SESSION_FAILURE:
return R.string.session_failure;
case DOWNGRADE_ATTACK:
return R.string.downgrade_attack;
case HOST_UNKNOWN: case HOST_UNKNOWN:
return R.string.account_status_host_unknown; return R.string.account_status_host_unknown;
case POLICY_VIOLATION: case POLICY_VIOLATION:

View File

@ -4,5 +4,5 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public interface OnIqPacketReceived extends PacketReceived { public interface OnIqPacketReceived extends PacketReceived {
public void onIqPacketReceived(Account account, IqPacket packet); void onIqPacketReceived(Account account, IqPacket packet);
} }

View File

@ -299,7 +299,7 @@ public class XmppConnection implements Runnable {
final SSLSession session = ((SSLSocket) localSocket).getSession(); final SSLSession session = ((SSLSocket) localSocket).getSession();
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) { if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
throw new SecurityException(); throw new StateChangingException(Account.State.TLS_ERROR);
} }
} catch (KeyManagementException e) { } catch (KeyManagementException e) {
features.encryptionEnabled = false; features.encryptionEnabled = false;
@ -388,7 +388,7 @@ public class XmppConnection implements Runnable {
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) localSocket).getSession())) { if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) localSocket).getSession())) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
throw new SecurityException(); throw new StateChangingException(Account.State.TLS_ERROR);
} }
} }
if (startXmpp(localSocket)) { if (startXmpp(localSocket)) {
@ -396,7 +396,7 @@ public class XmppConnection implements Runnable {
} else { } else {
localSocket.close(); localSocket.close();
} }
} catch (final SecurityException e) { } catch (final StateChangingException e) {
throw e; throw e;
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
@ -410,28 +410,14 @@ public class XmppConnection implements Runnable {
} }
} }
processStream(); processStream();
} catch (final java.lang.SecurityException e) {
this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
} catch (final RegistrationNotSupportedException e) {
this.changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
} catch (final IncompatibleServerException e) {
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
} catch (final SecurityException e) { } catch (final SecurityException e) {
this.changeStatus(Account.State.SECURITY_ERROR); this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
} catch (final UnauthorizedException e) { } catch(final StateChangingException e) {
this.changeStatus(Account.State.UNAUTHORIZED); this.changeStatus(e.state);
} catch (final PaymentRequiredException e) {
this.changeStatus(Account.State.PAYMENT_REQUIRED);
} 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.SocksProxyNotFoundException e) { } catch (final SocksSocketFactory.SocksProxyNotFoundException e) {
this.changeStatus(Account.State.TOR_NOT_AVAILABLE); this.changeStatus(Account.State.TOR_NOT_AVAILABLE);
} catch(final StreamErrorHostUnknown e) {
this.changeStatus(Account.State.HOST_UNKNOWN);
} catch(final StreamErrorPolicyViolation e) {
this.changeStatus(Account.State.POLICY_VIOLATION);
} catch(final StreamError e) {
this.changeStatus(Account.State.STREAM_ERROR);
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) { } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE); this.changeStatus(Account.State.OFFLINE);
@ -528,8 +514,8 @@ public class XmppConnection implements Runnable {
try { try {
saslMechanism.getResponse(challenge); saslMechanism.getResponse(challenge);
} catch (final SaslMechanism.AuthenticationException e) { } catch (final SaslMechanism.AuthenticationException e) {
disconnect(true);
Log.e(Config.LOGTAG, String.valueOf(e)); Log.e(Config.LOGTAG, String.valueOf(e));
throw new StateChangingException(Account.State.UNAUTHORIZED);
} }
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in"); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": logged in");
account.setKey(Account.PINNED_MECHANISM_KEY, account.setKey(Account.PINNED_MECHANISM_KEY,
@ -551,9 +537,9 @@ public class XmppConnection implements Runnable {
&& text.contains("renew") && text.contains("renew")
&& Config.MAGIC_CREATE_DOMAIN != null && Config.MAGIC_CREATE_DOMAIN != null
&& text.contains(Config.MAGIC_CREATE_DOMAIN)) { && text.contains(Config.MAGIC_CREATE_DOMAIN)) {
throw new PaymentRequiredException(); throw new StateChangingException(Account.State.PAYMENT_REQUIRED);
} else { } else {
throw new UnauthorizedException(); throw new StateChangingException(Account.State.UNAUTHORIZED);
} }
} else if (nextTag.isStart("challenge")) { } else if (nextTag.isStart("challenge")) {
final String challenge = tagReader.readElement(nextTag).getContent(); final String challenge = tagReader.readElement(nextTag).getContent();
@ -776,7 +762,11 @@ public class XmppConnection implements Runnable {
} }
} }
if (callback != null) { if (callback != null) {
try {
callback.onIqPacketReceived(account, packet); callback.onIqPacketReceived(account, packet);
} catch (StateChangingError error) {
throw new StateChangingException(error.state);
}
} }
} }
} }
@ -819,7 +809,7 @@ public class XmppConnection implements Runnable {
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) { if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed"); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
throw new SecurityException(); throw new StateChangingException(Account.State.TLS_ERROR);
} }
tagReader.setInputStream(sslSocket.getInputStream()); tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream());
@ -835,7 +825,7 @@ public class XmppConnection implements Runnable {
sslSocket.close(); sslSocket.close();
} catch (final NoSuchAlgorithmException | KeyManagementException e1) { } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
throw new SecurityException(); throw new StateChangingException(Account.State.TLS_ERROR);
} }
} }
@ -848,10 +838,10 @@ public class XmppConnection implements Runnable {
if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) { if (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS) {
sendRegistryRequest(); sendRegistryRequest();
} else { } else {
throw new IncompatibleServerException(); throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} }
} else if (!this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) { } else if (!this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
throw new RegistrationNotSupportedException(); throw new StateChangingException(Account.State.REGISTRATION_NOT_SUPPORTED);
} else if (this.streamFeatures.hasChild("mechanisms") } else if (this.streamFeatures.hasChild("mechanisms")
&& shouldAuthenticate && shouldAuthenticate
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) { && (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
@ -868,7 +858,7 @@ public class XmppConnection implements Runnable {
if (this.streamFeatures.hasChild("bind")) { if (this.streamFeatures.hasChild("bind")) {
sendBindRequest(); sendBindRequest();
} else { } else {
throw new IncompatibleServerException(); throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} }
} }
} }
@ -898,7 +888,7 @@ public class XmppConnection implements Runnable {
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) + " has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
") than pinned priority (" + pinnedMechanism + ") than pinned priority (" + pinnedMechanism +
"). Possible downgrade attack?"); "). Possible downgrade attack?");
throw new SecurityException(); throw new StateChangingException(Account.State.DOWNGRADE_ATTACK);
} }
Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism());
auth.setAttribute("mechanism", saslMechanism.getMechanism()); auth.setAttribute("mechanism", saslMechanism.getMechanism());
@ -907,7 +897,7 @@ public class XmppConnection implements Runnable {
} }
tagWriter.writeElement(auth); tagWriter.writeElement(auth);
} else { } else {
throw new IncompatibleServerException(); throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
} }
} }
@ -1042,8 +1032,7 @@ public class XmppConnection implements Runnable {
} else { } else {
Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString()); Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString());
} }
forceCloseSocket(); throw new StateChangingError(Account.State.BIND_FAILURE);
changeStatus(Account.State.BIND_FAILURE);
} }
}); });
} }
@ -1085,8 +1074,7 @@ public class XmppConnection implements Runnable {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IqPacket.TYPE.RESULT) {
sendPostBindInitialization(); sendPostBindInitialization();
} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) { } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); throw new StateChangingError(Account.State.SESSION_FAILURE);
disconnect(true);
} }
} }
}); });
@ -1266,12 +1254,12 @@ public class XmppConnection implements Runnable {
+ account.getResource() + ")"); + account.getResource() + ")");
throw new IOException(); throw new IOException();
} else if (streamError.hasChild("host-unknown")) { } else if (streamError.hasChild("host-unknown")) {
throw new StreamErrorHostUnknown(); throw new StateChangingException(Account.State.HOST_UNKNOWN);
} else if (streamError.hasChild("policy-violation")) { } else if (streamError.hasChild("policy-violation")) {
throw new StreamErrorPolicyViolation(); throw new StateChangingException(Account.State.POLICY_VIOLATION);
} else { } else {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString()); Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString());
throw new StreamError(); throw new StateChangingException(Account.State.STREAM_ERROR);
} }
} }
@ -1561,36 +1549,20 @@ public class XmppConnection implements Runnable {
return Identity.UNKNOWN; return Identity.UNKNOWN;
} }
private class UnauthorizedException extends IOException { private class StateChangingError extends Error {
private final Account.State state;
public StateChangingError(Account.State state) {
this.state = state;
}
} }
private class SecurityException extends IOException { private class StateChangingException extends IOException {
private final Account.State state;
public StateChangingException(Account.State state) {
this.state = state;
} }
private class IncompatibleServerException extends IOException {
}
private class StreamErrorHostUnknown extends StreamError {
}
private class StreamErrorPolicyViolation extends StreamError {
}
private class StreamError extends IOException {
}
private class PaymentRequiredException extends IOException {
}
private class RegistrationNotSupportedException extends IOException {
} }
public enum Identity { public enum Identity {

View File

@ -158,7 +158,7 @@
<string name="account_status_regis_conflict">Username already in use</string> <string name="account_status_regis_conflict">Username already in use</string>
<string name="account_status_regis_success">Registration completed</string> <string name="account_status_regis_success">Registration completed</string>
<string name="account_status_regis_not_sup">Server does not support registration</string> <string name="account_status_regis_not_sup">Server does not support registration</string>
<string name="account_status_security_error">Security error</string> <string name="account_status_tls_error">TLS error</string>
<string name="account_status_policy_violation">Policy violation</string> <string name="account_status_policy_violation">Policy violation</string>
<string name="account_status_incompatible_server">Incompatible server</string> <string name="account_status_incompatible_server">Incompatible server</string>
<string name="account_status_stream_error">Stream error</string> <string name="account_status_stream_error">Stream error</string>
@ -747,4 +747,6 @@
<string name="block_entire_domain">Block entire domain</string> <string name="block_entire_domain">Block entire domain</string>
<string name="online_right_now">online right now</string> <string name="online_right_now">online right now</string>
<string name="retry_decryption">Retry decryption</string> <string name="retry_decryption">Retry decryption</string>
<string name="session_failure">Session failure</string>
<string name="downgrade_attack">Downgrade attack</string>
</resources> </resources>