From 11b433d5d2c0a8ece066a6681e1cd44ef63f2cdc Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 30 Nov 2021 00:00:19 +0100 Subject: [PATCH 1/2] fix missing MX resolver eg. on android switching completely to /etc/resolv.conf (see #2780) does not work at least on some Androids (see https://github.com/deltachat/deltachat-android/issues/2151 ) therefore, we use the old approach as a fallback. --- src/provider.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/provider.rs b/src/provider.rs index be4c0f4ec1..afeafec434 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -4,7 +4,9 @@ mod data; use crate::config::Config; use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED}; -use async_std_resolver::resolver_from_system_conf; +use async_std_resolver::{ + config, resolver, resolver_from_system_conf, AsyncStdResolver, ResolveError, +}; use chrono::{NaiveDateTime, NaiveTime}; #[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)] @@ -81,6 +83,22 @@ pub struct Provider { pub oauth2_authorizer: Option, } +/// Get resolver to query MX records. +/// +/// We first try resolver_from_system_conf() which reads the system's resolver from `/etc/resolv.conf`. +/// This does not work at least on some Androids, therefore we use use ResolverConfig::default() +/// which default eg. to google's 8.8.8.8 or 8.8.4.4 as a fallback. +async fn get_resolver() -> Result { + if let Ok(resolver) = resolver_from_system_conf().await { + return Ok(resolver); + } + resolver( + config::ResolverConfig::default(), + config::ResolverOpts::default(), + ) + .await +} + /// Returns provider for the given domain. /// /// This function looks up domain in offline database first. If not @@ -118,7 +136,7 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> { /// /// For security reasons, only Gmail can be configured this way. pub async fn get_provider_by_mx(domain: &str) -> Option<&'static Provider> { - if let Ok(resolver) = resolver_from_system_conf().await { + if let Ok(resolver) = get_resolver().await { let mut fqdn: String = domain.to_string(); if !fqdn.ends_with('.') { fqdn.push('.'); @@ -169,6 +187,7 @@ mod tests { use super::*; use crate::dc_tools::time; + use anyhow::Result; use chrono::NaiveDate; #[test] @@ -242,4 +261,10 @@ mod tests { assert!(get_provider_update_timestamp() <= time()); assert!(get_provider_update_timestamp() > timestamp_past); } + + #[async_std::test] + async fn test_get_resolver() -> Result<()> { + assert!(get_resolver().await.is_ok()); + Ok(()) + } } From 1297ed025f91f559b39894875a09c28a95814bef Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 30 Nov 2021 00:29:05 +0100 Subject: [PATCH 2/2] log a warning, when we again have problems with figuring out MX resolvers --- deltachat-ffi/src/lib.rs | 6 +++++- examples/repl/cmdline.rs | 2 +- src/configure.rs | 4 +++- src/oauth2.rs | 27 +++++++++++++++------------ src/provider.rs | 21 +++++++++++++++------ 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 3c5ad5447c..e9d7e61627 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -3753,7 +3753,11 @@ pub unsafe extern "C" fn dc_provider_new_from_email( match socks5_enabled { Ok(socks5_enabled) => { - match block_on(provider::get_provider_info(addr.as_str(), socks5_enabled)) { + match block_on(provider::get_provider_info( + ctx, + addr.as_str(), + socks5_enabled, + )) { Some(provider) => provider, None => ptr::null_mut(), } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 0b538f19d4..3cb9d61b3f 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1185,7 +1185,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu let socks5_enabled = context .get_config_bool(config::Config::Socks5Enabled) .await?; - match provider::get_provider_info(arg1, socks5_enabled).await { + match provider::get_provider_info(&context, arg1, socks5_enabled).await { Some(info) => { println!("Information for provider belonging to {}:", arg1); println!("status: {}", info.status as u32); diff --git a/src/configure.rs b/src/configure.rs index 7b04dd9e83..358fbcc161 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -221,7 +221,9 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { "checking internal provider-info for offline autoconfig" ); - if let Some(provider) = provider::get_provider_info(¶m_domain, socks5_enabled).await { + if let Some(provider) = + provider::get_provider_info(ctx, ¶m_domain, socks5_enabled).await + { param.provider = Some(provider); match provider.status { provider::Status::Ok | provider::Status::Preparation => { diff --git a/src/oauth2.rs b/src/oauth2.rs index 11c4920386..aae7c7bdd3 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -58,7 +58,7 @@ pub async fn dc_get_oauth2_url( redirect_uri: &str, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - if let Some(oauth2) = Oauth2::from_address(addr, socks5_enabled).await { + if let Some(oauth2) = Oauth2::from_address(context, addr, socks5_enabled).await { context .sql .set_raw_config("oauth2_pending_redirect_uri", Some(redirect_uri)) @@ -79,7 +79,7 @@ pub async fn dc_get_oauth2_access_token( regenerate: bool, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - if let Some(oauth2) = Oauth2::from_address(addr, socks5_enabled).await { + if let Some(oauth2) = Oauth2::from_address(context, addr, socks5_enabled).await { let lock = context.oauth2_mutex.lock().await; // read generated token @@ -225,7 +225,7 @@ pub async fn dc_get_oauth2_addr( code: &str, ) -> Result> { let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?; - let oauth2 = match Oauth2::from_address(addr, socks5_enabled).await { + let oauth2 = match Oauth2::from_address(context, addr, socks5_enabled).await { Some(o) => o, None => return Ok(None), }; @@ -253,13 +253,13 @@ pub async fn dc_get_oauth2_addr( } impl Oauth2 { - async fn from_address(addr: &str, skip_mx: bool) -> Option { + async fn from_address(context: &Context, addr: &str, skip_mx: bool) -> Option { let addr_normalized = normalize_addr(addr); if let Some(domain) = addr_normalized .find('@') .map(|index| addr_normalized.split_at(index + 1).1) { - if let Some(oauth2_authorizer) = provider::get_provider_info(domain, skip_mx) + if let Some(oauth2_authorizer) = provider::get_provider_info(context, domain, skip_mx) .await .and_then(|provider| provider.oauth2_authorizer.as_ref()) { @@ -356,30 +356,33 @@ mod tests { #[async_std::test] async fn test_oauth_from_address() { + let t = TestContext::new().await; assert_eq!( - Oauth2::from_address("hello@gmail.com", false).await, + Oauth2::from_address(&t, "hello@gmail.com", false).await, Some(OAUTH2_GMAIL) ); assert_eq!( - Oauth2::from_address("hello@googlemail.com", false).await, + Oauth2::from_address(&t, "hello@googlemail.com", false).await, Some(OAUTH2_GMAIL) ); assert_eq!( - Oauth2::from_address("hello@yandex.com", false).await, + Oauth2::from_address(&t, "hello@yandex.com", false).await, Some(OAUTH2_YANDEX) ); assert_eq!( - Oauth2::from_address("hello@yandex.ru", false).await, + Oauth2::from_address(&t, "hello@yandex.ru", false).await, Some(OAUTH2_YANDEX) ); - - assert_eq!(Oauth2::from_address("hello@web.de", false).await, None); + assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None); } #[async_std::test] async fn test_oauth_from_mx() { + // TODO: this does not test MX lookup, google.com is in our provider-db + // does anyone know a "good" Google Workspace (former G Suite) domain we can use for testing? + let t = TestContext::new().await; assert_eq!( - Oauth2::from_address("hello@google.com", false).await, + Oauth2::from_address(&t, "hello@google.com", false).await, Some(OAUTH2_GMAIL) ); } diff --git a/src/provider.rs b/src/provider.rs index afeafec434..22def35956 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -3,6 +3,7 @@ mod data; use crate::config::Config; +use crate::context::Context; use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED}; use async_std_resolver::{ config, resolver, resolver_from_system_conf, AsyncStdResolver, ResolveError, @@ -107,7 +108,11 @@ async fn get_resolver() -> Result { /// /// For compatibility, email address can be passed to this function /// instead of the domain. -pub async fn get_provider_info(domain: &str, skip_mx: bool) -> Option<&'static Provider> { +pub async fn get_provider_info( + context: &Context, + domain: &str, + skip_mx: bool, +) -> Option<&'static Provider> { let domain = domain.rsplitn(2, '@').next()?; if let Some(provider) = get_provider_by_domain(domain) { @@ -115,7 +120,7 @@ pub async fn get_provider_info(domain: &str, skip_mx: bool) -> Option<&'static P } if !skip_mx { - if let Some(provider) = get_provider_by_mx(domain).await { + if let Some(provider) = get_provider_by_mx(context, domain).await { return Some(provider); } } @@ -135,7 +140,7 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> { /// Finds a provider based on MX record for the given domain. /// /// For security reasons, only Gmail can be configured this way. -pub async fn get_provider_by_mx(domain: &str) -> Option<&'static Provider> { +pub async fn get_provider_by_mx(context: &Context, domain: &str) -> Option<&'static Provider> { if let Ok(resolver) = get_resolver().await { let mut fqdn: String = domain.to_string(); if !fqdn.ends_with('.') { @@ -161,6 +166,8 @@ pub async fn get_provider_by_mx(domain: &str) -> Option<&'static Provider> { } } } + } else { + warn!(context, "cannot get a resolver to check MX records."); } None @@ -187,6 +194,7 @@ mod tests { use super::*; use crate::dc_tools::time; + use crate::test_utils::TestContext; use anyhow::Result; use chrono::NaiveDate; @@ -237,12 +245,13 @@ mod tests { #[async_std::test] async fn test_get_provider_info() { - assert!(get_provider_info("", false).await.is_none()); - assert!(get_provider_info("google.com", false).await.unwrap().id == "gmail"); + let t = TestContext::new().await; + assert!(get_provider_info(&t, "", false).await.is_none()); + assert!(get_provider_info(&t, "google.com", false).await.unwrap().id == "gmail"); // get_provider_info() accepts email addresses for backwards compatibility assert!( - get_provider_info("example@google.com", false) + get_provider_info(&t, "example@google.com", false) .await .unwrap() .id