diff --git a/app/src/main/java/eu/faircode/email/DnsHelper.java b/app/src/main/java/eu/faircode/email/DnsHelper.java index a8685f23a5..a330c48f2d 100644 --- a/app/src/main/java/eu/faircode/email/DnsHelper.java +++ b/app/src/main/java/eu/faircode/email/DnsHelper.java @@ -210,7 +210,7 @@ public class DnsHelper { result.add(new DnsRecord(soa.getHost().toString(true))); } else if (record instanceof SRVRecord) { SRVRecord srv = (SRVRecord) record; - result.add(new DnsRecord(srv.getTarget().toString(true), srv.getPort())); + result.add(new DnsRecord(srv.getTarget().toString(true), srv.getPort(), srv.getPriority(), srv.getWeight())); } else if (record instanceof TXTRecord) { TXTRecord txt = (TXTRecord) record; for (Object content : txt.getStrings()) { @@ -241,6 +241,9 @@ public class DnsHelper { throw new IllegalArgumentException(record.getClass().getName()); } + for (DnsRecord record : result) + record.query = name; + return result.toArray(new DnsRecord[0]); } catch (TextParseException ex) { Log.e(ex); @@ -283,8 +286,11 @@ public class DnsHelper { } static class DnsRecord { + String query; String name; Integer port; + Integer priority; + Integer weight; DnsRecord(String name) { this.name = name; @@ -294,5 +300,18 @@ public class DnsHelper { this.name = name; this.port = port; } + + DnsRecord(String name, int port, int priority, int weight) { + this.name = name; + this.port = port; + this.priority = priority; + this.weight = weight; + } + + @NonNull + @Override + public String toString() { + return query + "=" + name + ":" + port + " " + priority + "/" + weight; + } } } diff --git a/app/src/main/java/eu/faircode/email/EmailProvider.java b/app/src/main/java/eu/faircode/email/EmailProvider.java index 08769a931d..1f39382ea3 100644 --- a/app/src/main/java/eu/faircode/email/EmailProvider.java +++ b/app/src/main/java/eu/faircode/email/EmailProvider.java @@ -857,56 +857,81 @@ public class EmailProvider implements Parcelable { EmailProvider provider = new EmailProvider(domain); if (discover == Discover.ALL || discover == Discover.IMAP) { - try { - // Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server. - intf.onStatus("SRV imaps " + domain); - DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_imaps._tcp." + domain, "srv"); - if (records.length == 0) - throw new UnknownHostException(domain); - // ... service is not supported at all at a particular domain by setting the target of an SRV RR to "." - provider.imap.score = 50; - provider.imap.host = records[0].name; - provider.imap.port = records[0].port; - provider.imap.starttls = false; - EntityLog.log(context, "_imaps._tcp." + domain + "=" + provider.imap); - } catch (UnknownHostException ignored) { - // Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command - intf.onStatus("SRV imap " + domain); - DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_imap._tcp." + domain, "srv"); - if (records.length == 0) - throw new UnknownHostException(domain); - provider.imap.score = 50; - provider.imap.host = records[0].name; - provider.imap.port = records[0].port; - provider.imap.starttls = (provider.imap.port == 143); - EntityLog.log(context, "_imap._tcp." + domain + "=" + provider.imap); - } + intf.onStatus("SRV imap " + domain); + + // Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server. + List list = new ArrayList<>(); + list.addAll(Arrays.asList(DnsHelper.lookup(context, "_imap._tcp." + domain, "srv"))); + list.addAll(Arrays.asList(DnsHelper.lookup(context, "_imaps._tcp." + domain, "srv"))); + + // ... service is not supported at all at a particular domain by setting the target of an SRV RR to "." + for (DnsHelper.DnsRecord record : new ArrayList<>(list)) + if (TextUtils.isEmpty(record.name) || ".".equals(record.name)) + list.remove(record); + + if (list.size() == 0) + throw new UnknownHostException(domain); + + Collections.sort(list, new Comparator() { + @Override + public int compare(DnsHelper.DnsRecord d1, DnsHelper.DnsRecord d2) { + int p = -Integer.compare(d1.priority, d2.priority); + if (p != 0) + return p; + int w = -Integer.compare(d1.weight, d2.weight); + if (w != 0) + return w; + return -Boolean.compare(d1.query.startsWith("_imaps._tcp."), d2.query.startsWith("_imaps._tcp.")); + } + }); + + DnsHelper.DnsRecord pref = list.get(0); + + provider.imap.score = 50; + provider.imap.host = pref.name; + provider.imap.port = pref.port; + provider.imap.starttls = (!pref.query.startsWith("_imaps._tcp.") && pref.port == 143); + EntityLog.log(context, pref.query + "=" + provider.imap); } - if (discover == Discover.ALL || discover == Discover.SMTP) - try { - // Note that this covers connections both with and without Transport Layer Security (TLS) - intf.onStatus("SRV smtp " + domain); - DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submission._tcp." + domain, "srv"); - if (records.length == 0) - throw new UnknownHostException(domain); - provider.smtp.score = 50; - provider.smtp.host = records[0].name; - provider.smtp.port = records[0].port; - provider.smtp.starttls = (provider.smtp.port == 587); - EntityLog.log(context, "_submission._tcp." + domain + "=" + provider.smtp); - } catch (UnknownHostException ignored) { - // https://tools.ietf.org/html/rfc8314 - intf.onStatus("SRV smtps " + domain); - DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submissions._tcp." + domain, "srv"); - if (records.length == 0) - throw new UnknownHostException(domain); - provider.smtp.score = 50; - provider.smtp.host = records[0].name; - provider.smtp.port = records[0].port; - provider.smtp.starttls = false; - EntityLog.log(context, "_submissions._tcp." + domain + "=" + provider.smtp); - } + if (discover == Discover.ALL || discover == Discover.SMTP) { + intf.onStatus("SRV smtp " + domain); + // https://tools.ietf.org/html/rfc8314 + + List list = new ArrayList<>(); + // Note that this covers connections both with and without Transport Layer Security (TLS) + list.addAll(Arrays.asList(DnsHelper.lookup(context, "_submission._tcp." + domain, "srv"))); + list.addAll(Arrays.asList(DnsHelper.lookup(context, "_submissions._tcp." + domain, "srv"))); + + for (DnsHelper.DnsRecord record : new ArrayList<>(list)) + if (TextUtils.isEmpty(record.name) || ".".equals(record.name)) + list.remove(record); + + if (list.size() == 0) + throw new UnknownHostException(domain); + + Collections.sort(list, new Comparator() { + @Override + public int compare(DnsHelper.DnsRecord d1, DnsHelper.DnsRecord d2) { + int p = -Integer.compare(d1.priority, d2.priority); + if (p != 0) + return p; + int w = -Integer.compare(d1.weight, d2.weight); + if (w != 0) + return w; + // submission is being preferred + return -Boolean.compare(d1.query.startsWith("_submission._tcp."), d2.query.startsWith("_submission._tcp.")); + } + }); + + DnsHelper.DnsRecord pref = list.get(0); + + provider.smtp.score = 50; + provider.smtp.host = pref.name; + provider.smtp.port = pref.port; + provider.smtp.starttls = (!pref.query.startsWith("_submissions._tcp.") && pref.port == 587); + EntityLog.log(context, pref.query + "=" + provider.smtp); + } provider.validate();