|
|
@ -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(
|
|
|
|