mirror of
https://github.com/chatmail/core.git
synced 2026-05-01 20:36:31 +03:00
fix missing MX resolver eg. on android (#2852)
* 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. * log a warning, when we again have problems with figuring out MX resolvers
This commit is contained in:
@@ -3753,7 +3753,11 @@ pub unsafe extern "C" fn dc_provider_new_from_email(
|
|||||||
|
|
||||||
match socks5_enabled {
|
match socks5_enabled {
|
||||||
Ok(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,
|
Some(provider) => provider,
|
||||||
None => ptr::null_mut(),
|
None => ptr::null_mut(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1185,7 +1185,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
let socks5_enabled = context
|
let socks5_enabled = context
|
||||||
.get_config_bool(config::Config::Socks5Enabled)
|
.get_config_bool(config::Config::Socks5Enabled)
|
||||||
.await?;
|
.await?;
|
||||||
match provider::get_provider_info(arg1, socks5_enabled).await {
|
match provider::get_provider_info(&context, arg1, socks5_enabled).await {
|
||||||
Some(info) => {
|
Some(info) => {
|
||||||
println!("Information for provider belonging to {}:", arg1);
|
println!("Information for provider belonging to {}:", arg1);
|
||||||
println!("status: {}", info.status as u32);
|
println!("status: {}", info.status as u32);
|
||||||
|
|||||||
@@ -221,7 +221,9 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
|||||||
"checking internal provider-info for offline autoconfig"
|
"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);
|
param.provider = Some(provider);
|
||||||
match provider.status {
|
match provider.status {
|
||||||
provider::Status::Ok | provider::Status::Preparation => {
|
provider::Status::Ok | provider::Status::Preparation => {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub async fn dc_get_oauth2_url(
|
|||||||
redirect_uri: &str,
|
redirect_uri: &str,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?;
|
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
|
context
|
||||||
.sql
|
.sql
|
||||||
.set_raw_config("oauth2_pending_redirect_uri", Some(redirect_uri))
|
.set_raw_config("oauth2_pending_redirect_uri", Some(redirect_uri))
|
||||||
@@ -79,7 +79,7 @@ pub async fn dc_get_oauth2_access_token(
|
|||||||
regenerate: bool,
|
regenerate: bool,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?;
|
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;
|
let lock = context.oauth2_mutex.lock().await;
|
||||||
|
|
||||||
// read generated token
|
// read generated token
|
||||||
@@ -225,7 +225,7 @@ pub async fn dc_get_oauth2_addr(
|
|||||||
code: &str,
|
code: &str,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
let socks5_enabled = context.get_config_bool(Config::Socks5Enabled).await?;
|
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,
|
Some(o) => o,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
@@ -253,13 +253,13 @@ pub async fn dc_get_oauth2_addr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Oauth2 {
|
impl Oauth2 {
|
||||||
async fn from_address(addr: &str, skip_mx: bool) -> Option<Self> {
|
async fn from_address(context: &Context, addr: &str, skip_mx: bool) -> Option<Self> {
|
||||||
let addr_normalized = normalize_addr(addr);
|
let addr_normalized = normalize_addr(addr);
|
||||||
if let Some(domain) = addr_normalized
|
if let Some(domain) = addr_normalized
|
||||||
.find('@')
|
.find('@')
|
||||||
.map(|index| addr_normalized.split_at(index + 1).1)
|
.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
|
.await
|
||||||
.and_then(|provider| provider.oauth2_authorizer.as_ref())
|
.and_then(|provider| provider.oauth2_authorizer.as_ref())
|
||||||
{
|
{
|
||||||
@@ -356,30 +356,33 @@ mod tests {
|
|||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_oauth_from_address() {
|
async fn test_oauth_from_address() {
|
||||||
|
let t = TestContext::new().await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Oauth2::from_address("hello@gmail.com", false).await,
|
Oauth2::from_address(&t, "hello@gmail.com", false).await,
|
||||||
Some(OAUTH2_GMAIL)
|
Some(OAUTH2_GMAIL)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Oauth2::from_address("hello@googlemail.com", false).await,
|
Oauth2::from_address(&t, "hello@googlemail.com", false).await,
|
||||||
Some(OAUTH2_GMAIL)
|
Some(OAUTH2_GMAIL)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Oauth2::from_address("hello@yandex.com", false).await,
|
Oauth2::from_address(&t, "hello@yandex.com", false).await,
|
||||||
Some(OAUTH2_YANDEX)
|
Some(OAUTH2_YANDEX)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Oauth2::from_address("hello@yandex.ru", false).await,
|
Oauth2::from_address(&t, "hello@yandex.ru", false).await,
|
||||||
Some(OAUTH2_YANDEX)
|
Some(OAUTH2_YANDEX)
|
||||||
);
|
);
|
||||||
|
assert_eq!(Oauth2::from_address(&t, "hello@web.de", false).await, None);
|
||||||
assert_eq!(Oauth2::from_address("hello@web.de", false).await, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_oauth_from_mx() {
|
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!(
|
assert_eq!(
|
||||||
Oauth2::from_address("hello@google.com", false).await,
|
Oauth2::from_address(&t, "hello@google.com", false).await,
|
||||||
Some(OAUTH2_GMAIL)
|
Some(OAUTH2_GMAIL)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
mod data;
|
mod data;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::context::Context;
|
||||||
use crate::provider::data::{PROVIDER_DATA, PROVIDER_IDS, PROVIDER_UPDATED};
|
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};
|
use chrono::{NaiveDateTime, NaiveTime};
|
||||||
|
|
||||||
#[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
#[derive(Debug, Display, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||||
@@ -81,6 +84,22 @@ pub struct Provider {
|
|||||||
pub oauth2_authorizer: Option<Oauth2Authorizer>,
|
pub oauth2_authorizer: Option<Oauth2Authorizer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<AsyncStdResolver, ResolveError> {
|
||||||
|
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.
|
/// Returns provider for the given domain.
|
||||||
///
|
///
|
||||||
/// This function looks up domain in offline database first. If not
|
/// This function looks up domain in offline database first. If not
|
||||||
@@ -89,7 +108,11 @@ pub struct Provider {
|
|||||||
///
|
///
|
||||||
/// For compatibility, email address can be passed to this function
|
/// For compatibility, email address can be passed to this function
|
||||||
/// instead of the domain.
|
/// 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()?;
|
let domain = domain.rsplitn(2, '@').next()?;
|
||||||
|
|
||||||
if let Some(provider) = get_provider_by_domain(domain) {
|
if let Some(provider) = get_provider_by_domain(domain) {
|
||||||
@@ -97,7 +120,7 @@ pub async fn get_provider_info(domain: &str, skip_mx: bool) -> Option<&'static P
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !skip_mx {
|
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);
|
return Some(provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,8 +140,8 @@ pub fn get_provider_by_domain(domain: &str) -> Option<&'static Provider> {
|
|||||||
/// Finds a provider based on MX record for the given domain.
|
/// Finds a provider based on MX record for the given domain.
|
||||||
///
|
///
|
||||||
/// For security reasons, only Gmail can be configured this way.
|
/// 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) = resolver_from_system_conf().await {
|
if let Ok(resolver) = get_resolver().await {
|
||||||
let mut fqdn: String = domain.to_string();
|
let mut fqdn: String = domain.to_string();
|
||||||
if !fqdn.ends_with('.') {
|
if !fqdn.ends_with('.') {
|
||||||
fqdn.push('.');
|
fqdn.push('.');
|
||||||
@@ -143,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
|
None
|
||||||
@@ -169,6 +194,8 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dc_tools::time;
|
use crate::dc_tools::time;
|
||||||
|
use crate::test_utils::TestContext;
|
||||||
|
use anyhow::Result;
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -218,12 +245,13 @@ mod tests {
|
|||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_get_provider_info() {
|
async fn test_get_provider_info() {
|
||||||
assert!(get_provider_info("", false).await.is_none());
|
let t = TestContext::new().await;
|
||||||
assert!(get_provider_info("google.com", false).await.unwrap().id == "gmail");
|
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
|
// get_provider_info() accepts email addresses for backwards compatibility
|
||||||
assert!(
|
assert!(
|
||||||
get_provider_info("example@google.com", false)
|
get_provider_info(&t, "example@google.com", false)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.id
|
.id
|
||||||
@@ -242,4 +270,10 @@ mod tests {
|
|||||||
assert!(get_provider_update_timestamp() <= time());
|
assert!(get_provider_update_timestamp() <= time());
|
||||||
assert!(get_provider_update_timestamp() > timestamp_past);
|
assert!(get_provider_update_timestamp() > timestamp_past);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_get_resolver() -> Result<()> {
|
||||||
|
assert!(get_resolver().await.is_ok());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user