fix: do not request ALPN on standard ports and when using STARTTLS

Apparently some providers fail TLS connection
with "no_application_protocol" alert
even when requesting "imap" protocol for IMAP connection
and "smtp" protocol for SMTP connection.

Fixes <https://github.com/deltachat/deltachat-core-rust/issues/5892>.
This commit is contained in:
link2xt
2024-08-17 11:54:09 +00:00
parent af4d54ab50
commit cb4b992204
4 changed files with 31 additions and 11 deletions

View File

@@ -38,6 +38,16 @@ impl DerefMut for Client {
}
}
/// Converts port number to ALPN list.
fn alpn(port: u16) -> &'static [&'static str] {
if port == 993 {
// Do not request ALPN on standard port.
&[]
} else {
&["imap"]
}
}
/// Determine server capabilities.
///
/// If server supports ID capability, send our client ID.
@@ -157,7 +167,7 @@ impl Client {
}
async fn connect_secure(addr: SocketAddr, hostname: &str, strict_tls: bool) -> Result<Self> {
let tls_stream = connect_tls_inner(addr, hostname, strict_tls, "imap").await?;
let tls_stream = connect_tls_inner(addr, hostname, strict_tls, alpn(addr.port())).await?;
let buffered_stream = BufWriter::new(tls_stream);
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
let mut client = Client::new(session_stream);
@@ -197,7 +207,7 @@ impl Client {
let buffered_tcp_stream = client.into_inner();
let tcp_stream = buffered_tcp_stream.into_inner();
let tls_stream = wrap_tls(strict_tls, host, "imap", tcp_stream)
let tls_stream = wrap_tls(strict_tls, host, &[], tcp_stream)
.await
.context("STARTTLS upgrade failed")?;
@@ -217,7 +227,7 @@ impl Client {
let socks5_stream = socks5_config
.connect(context, domain, port, strict_tls)
.await?;
let tls_stream = wrap_tls(strict_tls, domain, "imap", socks5_stream).await?;
let tls_stream = wrap_tls(strict_tls, domain, alpn(port), socks5_stream).await?;
let buffered_stream = BufWriter::new(tls_stream);
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
let mut client = Client::new(session_stream);
@@ -270,7 +280,7 @@ impl Client {
let buffered_socks5_stream = client.into_inner();
let socks5_stream: Socks5Stream<_> = buffered_socks5_stream.into_inner();
let tls_stream = wrap_tls(strict_tls, hostname, "imap", socks5_stream)
let tls_stream = wrap_tls(strict_tls, hostname, &[], socks5_stream)
.await
.context("STARTTLS upgrade failed")?;
let buffered_stream = BufWriter::new(tls_stream);

View File

@@ -114,7 +114,7 @@ pub(crate) async fn connect_tls_inner(
addr: SocketAddr,
host: &str,
strict_tls: bool,
alpn: &str,
alpn: &[&str],
) -> Result<TlsStream<Pin<Box<TimeoutStream<TcpStream>>>>> {
let tcp_stream = connect_tcp_inner(addr).await?;
let tls_stream = wrap_tls(strict_tls, host, alpn, tcp_stream).await?;

View File

@@ -32,10 +32,10 @@ pub fn build_tls(strict_tls: bool, alpns: &[&str]) -> TlsConnector {
pub async fn wrap_tls<T: AsyncRead + AsyncWrite + Unpin>(
strict_tls: bool,
hostname: &str,
alpn: &str,
alpn: &[&str],
stream: T,
) -> Result<TlsStream<T>> {
let tls = build_tls(strict_tls, &[alpn]);
let tls = build_tls(strict_tls, alpn);
let tls_stream = tls.connect(hostname, stream).await?;
Ok(tls_stream)
}

View File

@@ -16,6 +16,16 @@ use crate::provider::Socket;
use crate::socks::Socks5Config;
use crate::tools::time;
/// Converts port number to ALPN list.
fn alpn(port: u16) -> &'static [&'static str] {
if port == 465 {
// Do not request ALPN on standard port.
&[]
} else {
&["smtp"]
}
}
/// Returns TLS, STARTTLS or plaintext connection
/// using SOCKS5 or direct connection depending on the given configuration.
///
@@ -113,7 +123,7 @@ async fn connect_secure_socks5(
let socks5_stream = socks5_config
.connect(context, hostname, port, strict_tls)
.await?;
let tls_stream = wrap_tls(strict_tls, hostname, "smtp", socks5_stream).await?;
let tls_stream = wrap_tls(strict_tls, hostname, alpn(port), socks5_stream).await?;
let mut buffered_stream = BufStream::new(tls_stream);
skip_smtp_greeting(&mut buffered_stream).await?;
let session_stream: Box<dyn SessionBufStream> = Box::new(buffered_stream);
@@ -135,7 +145,7 @@ async fn connect_starttls_socks5(
let client = SmtpClient::new().smtp_utf8(true);
let transport = SmtpTransport::new(client, BufStream::new(socks5_stream)).await?;
let tcp_stream = transport.starttls().await?.into_inner();
let tls_stream = wrap_tls(strict_tls, hostname, "smtp", tcp_stream)
let tls_stream = wrap_tls(strict_tls, hostname, &[], tcp_stream)
.await
.context("STARTTLS upgrade failed")?;
let buffered_stream = BufStream::new(tls_stream);
@@ -163,7 +173,7 @@ async fn connect_secure(
hostname: &str,
strict_tls: bool,
) -> Result<Box<dyn SessionBufStream>> {
let tls_stream = connect_tls_inner(addr, hostname, strict_tls, "smtp").await?;
let tls_stream = connect_tls_inner(addr, hostname, strict_tls, alpn(addr.port())).await?;
let mut buffered_stream = BufStream::new(tls_stream);
skip_smtp_greeting(&mut buffered_stream).await?;
let session_stream: Box<dyn SessionBufStream> = Box::new(buffered_stream);
@@ -181,7 +191,7 @@ async fn connect_starttls(
let client = async_smtp::SmtpClient::new().smtp_utf8(true);
let transport = async_smtp::SmtpTransport::new(client, BufStream::new(tcp_stream)).await?;
let tcp_stream = transport.starttls().await?.into_inner();
let tls_stream = wrap_tls(strict_tls, host, "smtp", tcp_stream)
let tls_stream = wrap_tls(strict_tls, host, &[], tcp_stream)
.await
.context("STARTTLS upgrade failed")?;