From 41eefbe3b47228e3e043f4840e0a8c65b41748a2 Mon Sep 17 00:00:00 2001 From: M66B Date: Thu, 6 Jan 2022 22:28:22 +0100 Subject: [PATCH] Experiment: TLS detection --- .../eu/faircode/email/ConnectionHelper.java | 2 + app/src/main/java/eu/faircode/email/Core.java | 3 + .../java/eu/faircode/email/MessageHelper.java | 91 +++++++++++++++++++ app/src/main/jni/fairemail.cc | 27 ++++++ 4 files changed, 123 insertions(+) diff --git a/app/src/main/java/eu/faircode/email/ConnectionHelper.java b/app/src/main/java/eu/faircode/email/ConnectionHelper.java index f9f9d632a1..50d74437a7 100644 --- a/app/src/main/java/eu/faircode/email/ConnectionHelper.java +++ b/app/src/main/java/eu/faircode/email/ConnectionHelper.java @@ -101,6 +101,8 @@ public class ConnectionHelper { public static native int jni_socket_get_send_buffer(int fd); + public static native boolean jni_is_numeric_address(String _ip); + static class NetworkState { private Boolean connected = null; private Boolean suitable = null; diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java index 8eb80a6912..9e68842d56 100644 --- a/app/src/main/java/eu/faircode/email/Core.java +++ b/app/src/main/java/eu/faircode/email/Core.java @@ -3738,6 +3738,9 @@ class Core { if (received == null) received = 0L; + if (BuildConfig.DEBUG) + Log.i("--- TLS=" + helper.getTLS()); + String[] authentication = helper.getAuthentication(); MessageHelper.MessageParts parts = helper.getMessageParts(); diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 862ce938de..f48d2d09e3 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -66,7 +66,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.IDN; +import java.net.InetAddress; import java.net.URLDecoder; +import java.net.UnknownHostException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; @@ -1914,6 +1916,95 @@ public class MessageHelper { return null; } + Boolean getTLS() throws MessagingException { + // https://datatracker.ietf.org/doc/html/rfc2821#section-4.4 + + // Time-stamp-line = "Received:" FWS Stamp + // Stamp = From-domain By-domain Opt-info ";" FWS date-time + // From-domain = "FROM" FWS Extended-Domain CFWS + // By-domain = "BY" FWS Extended-Domain CFWS + // Opt-info = [Via] [With] [ID] [For] + // Via = "VIA" FWS Link CFWS + // With = "WITH" FWS Protocol CFWS + // ID = "ID" FWS String / msg-id CFWS + // For = "FOR" FWS 1*( Path / Mailbox ) CFWS + + // Extended-Domain = Domain / ( Domain FWS "(" TCP-info ")" ) / ( Address-literal FWS "(" TCP-info ")" ) + // TCP-info = Address-literal / ( Domain FWS Address-literal ) + // Link = "TCP" / Addtl-Link + // Addtl-Link = Atom + // Protocol = "ESMTP" / "SMTP" / Attdl-Protocol + // Attdl-Protocol = Atom + + String[] received = imessage.getHeader("Received"); + if (received == null || received.length == 0) + return null; + + final List words = Collections.unmodifiableList(Arrays.asList( + "from", "by", "via", "with", "id", "for")); + + // First header is last added header + for (String r : received) { + String header = MimeUtility.unfold(r); + + int s, e; + do { + s = header.indexOf('('); + e = header.indexOf(')', s); + if (s > 0 && e > 0) + header = header.substring(0, s) + header.substring((e + 1)); + } while (s > 0 && e > 0); + + int semi = header.lastIndexOf(';'); + if (semi > 0) + header = header.substring(0, semi); + + header = header.replaceAll("\\s+", " "); + + Log.i("--- header=" + header); + + String[] parts = header.split(" "); + boolean hasFrom = false; + boolean hasWith = false; + for (int j = 0; j < parts.length - 1; j++) { + Log.i("--- part " + j + " " + parts[j] + "=" + parts[j + 1]); + if ("from".equalsIgnoreCase(parts[j])) { + hasFrom = true; + String from = parts[j + 1].toLowerCase(Locale.ROOT); + boolean numeric = ConnectionHelper.jni_is_numeric_address(from); + Log.i("--- numeric=" + numeric); + if (numeric) + try { + InetAddress addr = InetAddress.getByName(from); + if (addr.isSiteLocalAddress()) + break; + Log.i("--- remote"); + } catch (UnknownHostException ex) { + Log.e(ex); + } + } else if ("with".equalsIgnoreCase(parts[j])) { + hasWith = true; + if (hasFrom) { + String with = parts[j + 1].toUpperCase(Locale.ROOT); + Log.i("--- MTP=" + with.contains("MTP") + " MTPS=" + with.contains("MTPS")); + // https://www.iana.org/assignments/mail-parameters/mail-parameters.txt + if (!with.contains("MTP")) + return null; + if (!with.contains("MTPS")) + return false; + } + break; + } + if (words.contains(parts[j])) + j++; + } + if (hasFrom && !hasWith) + return null; + } + + return true; + } + Long getSent() throws MessagingException { ensureEnvelope(); diff --git a/app/src/main/jni/fairemail.cc b/app/src/main/jni/fairemail.cc index b1cbd31187..e90e17706d 100644 --- a/app/src/main/jni/fairemail.cc +++ b/app/src/main/jni/fairemail.cc @@ -157,3 +157,30 @@ Java_eu_faircode_email_ConnectionHelper_jni_1socket_1get_1send_1buffer( log_android(ANDROID_LOG_DEBUG, "ioctl(TIOCOUTQ) res=%d queued=%d", res, queued); return (res == 0 ? queued : 0); } + +extern "C" +JNIEXPORT jboolean JNICALL +Java_eu_faircode_email_ConnectionHelper_jni_1is_1numeric_1address( + JNIEnv *env, jclass clazz, + jstring _ip) { + jboolean numeric = 0; + const char *ip = env->GetStringUTFChars(_ip, 0); + + // https://linux.die.net/man/3/getaddrinfo + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; // suppresses any potentially lengthy network host address lookups + struct addrinfo *result; + int err = getaddrinfo(ip, nullptr, &hints, &result); + if (err) + log_android(ANDROID_LOG_DEBUG, "getaddrinfo(%s) error %d: %s", ip, err, gai_strerror(err)); + else + numeric = (jboolean) (result != nullptr); + + if (result != nullptr) + freeaddrinfo(result); + + env->ReleaseStringUTFChars(_ip, ip); + return numeric; +}