Switch to BC on unsupported protocol

master
M66B 2 days ago
parent 170cfce52d
commit 7a987e71e7

@ -73,6 +73,7 @@ import javax.mail.MessagingException;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
@ -504,6 +505,17 @@ public class ConnectionHelper {
return false; return false;
} }
static boolean isUnsupportedProtocol(Throwable ex) {
while (ex != null) {
if (ex instanceof SSLHandshakeException &&
ex.getMessage() != null &&
ex.getMessage().contains("UNSUPPORTED_PROTOCOL"))
return true;
ex = ex.getCause();
}
return false;
}
static boolean isAborted(Throwable ex) { static boolean isAborted(Throwable ex) {
while (ex != null) { while (ex != null) {
String msg = ex.getMessage(); String msg = ex.getMessage();

@ -742,80 +742,99 @@ public class EmailService implements AutoCloseable {
((ErrnoException) ex.getCause().getCause()).errno == OsConstants.EACCES) ((ErrnoException) ex.getCause().getCause()).errno == OsConstants.EACCES)
throw new SecurityException("EACCES Please check 'Restrict data usage' in the Android app settings", ex); throw new SecurityException("EACCES Please check 'Restrict data usage' in the Android app settings", ex);
boolean ioError = false; if (!ssl_harden && ConnectionHelper.isUnsupportedProtocol(ex)) {
Throwable ce = ex; EntityLog.log(context, EntityLog.Type.Network, "Unsuported protocol");
while (ce != null) {
if (factory != null &&
(ce instanceof CertificateException ||
ce instanceof CertPathValidatorException))
throw new UntrustedException(ex, factory.certificate);
if (ce instanceof IOException)
ioError = true;
ce = ce.getCause();
}
if (ioError) {
EntityLog.log(context, EntityLog.Type.Network, "Connect ex=" +
ex.getClass().getName() + ":" +
ex + "\n" + android.util.Log.getStackTraceString(ex));
try { try {
// Some devices resolve IPv6 addresses while not having IPv6 connectivity this.insecure = true;
InetAddress[] iaddrs = DnsHelper.getAllByName(context, host, dnssec); factory = new SSLSocketFactoryService(context,
int ip4 = (main instanceof Inet4Address ? 1 : 0); host, port, true, false,
int ip6 = (main instanceof Inet6Address ? 1 : 0); false, false, false, false,
false,
boolean[] has46 = ConnectionHelper.has46(context); true, true,
factory.key, factory.chain, factory.trustedFingerprint);
boolean prefer_ip4 = prefs.getBoolean("prefer_ip4", true); properties.put("mail." + protocol + ".ssl.socketFactory", factory);
boolean prefer_ip6 = !prefer_ip4 && prefs.getBoolean("prefer_ip6", false); _connect(main, port, require_id, user, factory);
return;
EntityLog.log(context, EntityLog.Type.Network, "Address main=" + main + } catch (GeneralSecurityException ex1) {
" count=" + iaddrs.length + Log.e(ex1);
" ip4=" + ip4 + " max4=" + MAX_IPV4 + " has4=" + has46[0] + " pref4=" + prefer_ip4 + }
" ip6=" + ip6 + " max6=" + MAX_IPV6 + " has6=" + has46[1] + " pref6=" + prefer_ip6); return;
} else {
if (prefer_ip4 || prefer_ip6) boolean ioError = false;
Arrays.sort(iaddrs, new Comparator<InetAddress>() { Throwable ce = ex;
@Override while (ce != null) {
public int compare(InetAddress a1, InetAddress a2) { if (factory != null &&
int s = Boolean.compare(a1 instanceof Inet4Address, a2 instanceof Inet4Address); (ce instanceof CertificateException ||
if (prefer_ip4) ce instanceof CertPathValidatorException))
s = -s; throw new UntrustedException(ex, factory.certificate);
return s; if (ce instanceof IOException)
} ioError = true;
}); ce = ce.getCause();
}
for (InetAddress iaddr : iaddrs) {
EntityLog.log(context, EntityLog.Type.Network, "Address resolved=" + iaddr);
if (iaddr.equals(main))
continue;
if (iaddr instanceof Inet4Address) { if (ioError) {
if (!has46[0] || ip4 >= MAX_IPV4) EntityLog.log(context, EntityLog.Type.Network, "Connect ex=" +
ex.getClass().getName() + ":" +
ex + "\n" + android.util.Log.getStackTraceString(ex));
try {
// Some devices resolve IPv6 addresses while not having IPv6 connectivity
InetAddress[] iaddrs = DnsHelper.getAllByName(context, host, dnssec);
int ip4 = (main instanceof Inet4Address ? 1 : 0);
int ip6 = (main instanceof Inet6Address ? 1 : 0);
boolean[] has46 = ConnectionHelper.has46(context);
boolean prefer_ip4 = prefs.getBoolean("prefer_ip4", true);
boolean prefer_ip6 = !prefer_ip4 && prefs.getBoolean("prefer_ip6", false);
EntityLog.log(context, EntityLog.Type.Network, "Address main=" + main +
" count=" + iaddrs.length +
" ip4=" + ip4 + " max4=" + MAX_IPV4 + " has4=" + has46[0] + " pref4=" + prefer_ip4 +
" ip6=" + ip6 + " max6=" + MAX_IPV6 + " has6=" + has46[1] + " pref6=" + prefer_ip6);
if (prefer_ip4 || prefer_ip6)
Arrays.sort(iaddrs, new Comparator<InetAddress>() {
@Override
public int compare(InetAddress a1, InetAddress a2) {
int s = Boolean.compare(a1 instanceof Inet4Address, a2 instanceof Inet4Address);
if (prefer_ip4)
s = -s;
return s;
}
});
for (InetAddress iaddr : iaddrs) {
EntityLog.log(context, EntityLog.Type.Network, "Address resolved=" + iaddr);
if (iaddr.equals(main))
continue; continue;
ip4++;
}
if (iaddr instanceof Inet6Address) { if (iaddr instanceof Inet4Address) {
if (!has46[1] || ip6 >= MAX_IPV6) if (!has46[0] || ip4 >= MAX_IPV4)
continue; continue;
ip6++; ip4++;
} }
if (iaddr instanceof Inet6Address) {
if (!has46[1] || ip6 >= MAX_IPV6)
continue;
ip6++;
}
try { try {
EntityLog.log(context, EntityLog.Type.Network, "Falling back to " + iaddr); EntityLog.log(context, EntityLog.Type.Network, "Falling back to " + iaddr);
_connect(iaddr, port, require_id, user, factory); _connect(iaddr, port, require_id, user, factory);
return; return;
} catch (MessagingException ex1) { } catch (MessagingException ex1) {
ex = ex1; ex = ex1;
EntityLog.log(context, EntityLog.Type.Network, "Fallback ex=" + EntityLog.log(context, EntityLog.Type.Network, "Fallback ex=" +
ex1.getClass().getName() + ":" + ex1.getClass().getName() + ":" +
ex1 + " " + android.util.Log.getStackTraceString(ex1)); ex1 + " " + android.util.Log.getStackTraceString(ex1));
}
} }
} catch (IOException ex1) {
throw new MessagingException(ex1.getMessage(), ex1);
} }
} catch (IOException ex1) {
throw new MessagingException(ex1.getMessage(), ex1);
} }
} }
@ -1111,6 +1130,8 @@ public class EmailService implements AutoCloseable {
private boolean secure; private boolean secure;
private boolean ssl_harden; private boolean ssl_harden;
private boolean ssl_harden_strict; private boolean ssl_harden_strict;
private PrivateKey key;
private X509Certificate[] chain;
private String trustedFingerprint; private String trustedFingerprint;
private SSLSocketFactory factory; private SSLSocketFactory factory;
private X509Certificate certificate; private X509Certificate certificate;
@ -1125,6 +1146,8 @@ public class EmailService implements AutoCloseable {
this.secure = !insecure; this.secure = !insecure;
this.ssl_harden = ssl_harden; this.ssl_harden = ssl_harden;
this.ssl_harden_strict = ssl_harden_strict; this.ssl_harden_strict = ssl_harden_strict;
this.key = key;
this.chain = chain;
this.trustedFingerprint = fingerprint; this.trustedFingerprint = fingerprint;
TrustManager[] tms = SSLHelper.getTrustManagers( TrustManager[] tms = SSLHelper.getTrustManagers(

Loading…
Cancel
Save