Import upstream v0.16.22, stripped

Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+349
View File
@@ -0,0 +1,349 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{
DEFAULT_TLS_TIMEOUT, Listener, Listeners, ServerProtocol, TcpListener,
tls::{TLS12_VERSION, TLS13_VERSION},
};
use crate::{
Inner,
network::{TcpAcceptor, tls::CertificateResolver},
};
use registry::{
schema::{
enums::{NetworkListenerProtocol, TlsCipherSuite, TlsVersion},
prelude::{ObjectType, SocketAddr},
structs::{ClusterListenerGroup, NetworkListener, SystemSettings},
},
types::{id::ObjectId, map::Map},
};
use rustls::{
ALL_VERSIONS, ServerConfig, SupportedCipherSuite,
crypto::aws_lc_rs::{ALL_CIPHER_SUITES, cipher_suite::*, default_provider},
};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr as StdSocketAddr},
str::FromStr,
sync::Arc,
};
use store::registry::{RegistryObject, bootstrap::Bootstrap};
use tokio::net::TcpSocket;
use tokio_rustls::TlsAcceptor;
use types::id::Id;
use utils::snowflake::SnowflakeIdGenerator;
impl Listeners {
pub async fn parse(bp: &mut Bootstrap) -> Self {
// Parse ACME managers
let mut servers = Listeners {
span_id_gen: Arc::new(SnowflakeIdGenerator::new()),
..Default::default()
};
// Parse servers
if !bp.registry.is_recovery_mode() {
let system = bp.setting_infallible::<SystemSettings>().await;
for listener in bp.list_infallible::<NetworkListener>().await {
if bp.role.as_ref().is_none_or(|r| match &r.listeners {
ClusterListenerGroup::EnableAll => true,
ClusterListenerGroup::DisableAll => false,
ClusterListenerGroup::EnableSome(group) => {
group.listener_ids.iter().any(|id| *id == listener.id.id())
}
ClusterListenerGroup::DisableSome(group) => {
!group.listener_ids.iter().any(|id| *id == listener.id.id())
}
}) {
servers.parse_server(bp, listener, &system);
}
}
} else {
servers.parse_server(
bp,
RegistryObject {
id: ObjectId::new(ObjectType::NetworkListener, Id::singleton()),
object: NetworkListener {
bind: Map::new(vec![
SocketAddr::from_str(&format!(
"[::]:{}",
std::env::var("STALWART_RECOVERY_MODE_PORT")
.ok()
.and_then(|p| p.parse::<u16>().ok())
.unwrap_or(8080)
))
.unwrap(),
]),
name: "http-recovery".to_string(),
protocol: NetworkListenerProtocol::Http,
tls_implicit: false,
..Default::default()
},
revision: 0,
},
&SystemSettings::default(),
);
}
servers
}
pub fn parse_server(
&mut self,
bp: &mut Bootstrap,
listener: RegistryObject<NetworkListener>,
system: &SystemSettings,
) {
let id = listener.id;
let revision = listener.revision;
let listener = listener.object;
// Parse protocol
let protocol = match listener.protocol {
NetworkListenerProtocol::Smtp => ServerProtocol::Smtp,
NetworkListenerProtocol::Lmtp => ServerProtocol::Lmtp,
NetworkListenerProtocol::Http => ServerProtocol::Http,
NetworkListenerProtocol::Imap => ServerProtocol::Imap,
NetworkListenerProtocol::Pop3 => ServerProtocol::Pop3,
NetworkListenerProtocol::ManageSieve => ServerProtocol::ManageSieve,
};
// Build listeners
let mut listeners = Vec::new();
for addr in listener.bind.iter() {
// Parse bind address and build socket
let mut addr = addr.0;
let socket = match if addr.is_ipv4() {
TcpSocket::new_v4()
} else {
TcpSocket::new_v6()
} {
Ok(socket) => socket,
Err(err)
if is_ipv6_unsupported(&err)
&& addr.is_ipv6()
&& addr.ip().is_unspecified() =>
{
let v4_addr =
StdSocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), addr.port());
bp.build_warning(
id,
format!(
"IPv6 unavailable on this host ({err}); \
falling back from {addr} to {v4_addr}"
),
);
addr = v4_addr;
match TcpSocket::new_v4() {
Ok(socket) => socket,
Err(err) => {
bp.build_error(
id,
format!("Failed to create IPv4 fallback socket: {err}"),
);
return;
}
}
}
Err(err) => {
bp.build_error(id, format!("Failed to create socket: {err}"));
return;
}
};
#[cfg(windows)]
if addr.is_ipv6()
&& addr.ip().is_unspecified()
&& let Err(err) = socket2::SockRef::from(&socket).set_only_v6(false)
{
bp.build_warning(
id,
format!(
"Failed to disable IPV6_V6ONLY on {addr} ({err}); \
IPv4 clients will not be able to connect to this listener"
),
);
}
if let Err(err) = socket.set_reuseaddr(listener.socket_reuse_address) {
bp.build_error(id, format!("Failed to set SO_REUSEADDR: {err}"));
return;
}
#[cfg(not(target_env = "msvc"))]
if let Err(err) = socket.set_reuseport(listener.socket_reuse_port) {
bp.build_error(id, format!("Failed to set SO_REUSEPORT: {err}"));
return;
}
if let Some(send_size) = listener.socket_send_buffer_size
&& let Err(err) = socket.set_send_buffer_size(send_size as u32)
{
bp.build_error(id, format!("Failed to set SO_SNDBUF: {err}"));
return;
}
if let Some(recv_size) = listener.socket_receive_buffer_size
&& let Err(err) = socket.set_recv_buffer_size(recv_size as u32)
{
bp.build_error(id, format!("Failed to set SO_RCVBUF: {err}"));
return;
}
if let Some(tos) = listener.socket_tos_v4
&& let Err(err) = socket.set_tos_v4(tos as u32)
{
bp.build_error(id, format!("Failed to set IP_TOS: {err}"));
return;
}
listeners.push(TcpListener {
socket,
addr,
ttl: listener.socket_ttl.map(|v| v as u32),
backlog: listener.socket_backlog.map(|v| v as u32),
nodelay: listener.socket_no_delay,
});
}
let span_id_gen = self.span_id_gen.clone();
self.servers.push(Listener {
max_connections: listener.max_connections.unwrap_or(system.max_connections),
tls_timeout: listener
.tls_timeout
.map_or(DEFAULT_TLS_TIMEOUT, |timeout| timeout.into_inner()),
id: listener.name.clone(),
registry_id: id,
protocol,
listeners,
proxy_networks: if !listener.override_proxy_trusted_networks.is_empty() {
listener.override_proxy_trusted_networks.as_slice().to_vec()
} else {
system.proxy_trusted_networks.as_slice().to_vec()
},
span_id_gen,
});
self.parsed_listeners.push(RegistryObject {
id,
object: listener,
revision,
});
}
pub async fn parse_tcp_acceptors(&mut self, bp: &mut Bootstrap, inner: Arc<Inner>) {
let resolver = Arc::new(CertificateResolver::new(inner.clone()));
for listener in std::mem::take(&mut self.parsed_listeners) {
let id = listener.id;
let listener = listener.object;
// Build TLS config
let acceptor = if listener.use_tls {
// Parse protocol versions
let mut tls_v2 = true;
let mut tls_v3 = true;
for disabled in listener.tls_disable_protocols {
match disabled {
TlsVersion::Tls12 => {
tls_v2 = false;
}
TlsVersion::Tls13 => {
tls_v3 = false;
}
}
}
// Parse cipher suites
let mut disabled_ciphers: Vec<SupportedCipherSuite> = Vec::new();
for disabled in listener.tls_disable_cipher_suites {
disabled_ciphers.push(match disabled {
TlsCipherSuite::Tls13Aes256GcmSha384 => TLS13_AES_256_GCM_SHA384,
TlsCipherSuite::Tls13Aes128GcmSha256 => TLS13_AES_128_GCM_SHA256,
TlsCipherSuite::Tls13Chacha20Poly1305Sha256 => {
TLS13_CHACHA20_POLY1305_SHA256
}
TlsCipherSuite::TlsEcdheEcdsaWithAes256GcmSha384 => {
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
}
TlsCipherSuite::TlsEcdheEcdsaWithAes128GcmSha256 => {
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
}
TlsCipherSuite::TlsEcdheEcdsaWithChacha20Poly1305Sha256 => {
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
}
TlsCipherSuite::TlsEcdheRsaWithAes256GcmSha384 => {
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
}
TlsCipherSuite::TlsEcdheRsaWithAes128GcmSha256 => {
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
}
TlsCipherSuite::TlsEcdheRsaWithChacha20Poly1305Sha256 => {
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
}
});
}
// Build cert provider
let mut provider = default_provider();
if !disabled_ciphers.is_empty() {
provider.cipher_suites = ALL_CIPHER_SUITES
.iter()
.filter(|suite| !disabled_ciphers.contains(suite))
.copied()
.collect();
}
// Build server config
let mut server_config = match ServerConfig::builder_with_provider(provider.into())
.with_protocol_versions(if tls_v3 == tls_v2 {
ALL_VERSIONS
} else if tls_v3 {
TLS13_VERSION
} else {
TLS12_VERSION
}) {
Ok(server_config) => server_config
.with_no_client_auth()
.with_cert_resolver(resolver.clone()),
Err(err) => {
bp.build_error(id, format!("Failed to build TLS server config: {err}"));
return;
}
};
server_config.ignore_client_order = listener.tls_ignore_client_order;
// Build acceptor
let default_config = Arc::new(server_config);
TcpAcceptor::Tls {
acceptor: TlsAcceptor::from(default_config.clone()),
config: default_config,
implicit: listener.tls_implicit,
}
} else {
TcpAcceptor::Plain
};
self.tcp_acceptors.insert(listener.name, acceptor);
}
}
}
fn is_ipv6_unsupported(err: &std::io::Error) -> bool {
let code = err.raw_os_error();
#[cfg(unix)]
{
matches!(code, Some(libc::EAFNOSUPPORT) | Some(libc::EPROTONOSUPPORT))
}
#[cfg(windows)]
{
matches!(code, Some(10047) | Some(10043))
}
#[cfg(not(any(unix, windows)))]
{
false
}
}
+83
View File
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::network::TcpAcceptor;
use ahash::AHashMap;
use registry::{
schema::structs::NetworkListener,
types::{id::ObjectId, ipmask::IpAddrOrMask},
};
use serde::{Deserialize, Serialize};
use std::{fmt::Display, net::SocketAddr, sync::Arc, time::Duration};
use store::registry::RegistryObject;
use tokio::net::TcpSocket;
use utils::snowflake::SnowflakeIdGenerator;
pub mod listener;
pub mod tls;
#[derive(Default)]
pub struct Listeners {
pub servers: Vec<Listener>,
pub tcp_acceptors: AHashMap<String, TcpAcceptor>,
pub span_id_gen: Arc<SnowflakeIdGenerator>,
parsed_listeners: Vec<RegistryObject<NetworkListener>>,
}
#[derive(Debug, Default)]
pub struct Listener {
pub registry_id: ObjectId,
pub id: String,
pub protocol: ServerProtocol,
pub listeners: Vec<TcpListener>,
pub proxy_networks: Vec<IpAddrOrMask>,
pub max_connections: u64,
pub tls_timeout: Duration,
pub span_id_gen: Arc<SnowflakeIdGenerator>,
}
pub const DEFAULT_TLS_TIMEOUT: Duration = Duration::from_secs(60);
#[derive(Debug)]
pub struct TcpListener {
pub socket: TcpSocket,
pub addr: SocketAddr,
pub backlog: Option<u32>,
// TCP options
pub ttl: Option<u32>,
pub nodelay: bool,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default, Serialize, Deserialize)]
pub enum ServerProtocol {
#[default]
Smtp,
Lmtp,
Imap,
Pop3,
Http,
ManageSieve,
}
impl ServerProtocol {
pub fn as_str(&self) -> &'static str {
match self {
ServerProtocol::Smtp => "smtp",
ServerProtocol::Lmtp => "lmtp",
ServerProtocol::Imap => "imap",
ServerProtocol::Http => "http",
ServerProtocol::Pop3 => "pop3",
ServerProtocol::ManageSieve => "managesieve",
}
}
}
impl Display for ServerProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
+278
View File
@@ -0,0 +1,278 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::network::acme::ParsedCert;
use ahash::{AHashMap, AHashSet};
use rcgen::generate_simple_self_signed;
use registry::{
schema::{
prelude::Object,
structs::{Certificate, PublicText, SecretText, SystemSettings},
},
types::{datetime::UTCDateTime, map::Map},
};
use rustls::{
SupportedProtocolVersion,
crypto::aws_lc_rs::sign::any_supported_type,
sign::CertifiedKey,
version::{TLS12, TLS13},
};
use rustls_pemfile::{Item, certs, read_all};
use rustls_pki_types::PrivateKeyDer;
use std::{io::Cursor, sync::Arc};
use store::{
registry::{bootstrap::Bootstrap, write::RegistryWrite},
write::now,
};
pub static TLS13_VERSION: &[&SupportedProtocolVersion] = &[&TLS13];
pub static TLS12_VERSION: &[&SupportedProtocolVersion] = &[&TLS12];
pub(crate) async fn parse_certificates(
bp: &mut Bootstrap,
certificates: &mut AHashMap<Box<str>, Arc<CertifiedKey>>,
subject_names: &mut AHashSet<Box<str>>,
) {
let system = bp.setting_infallible::<SystemSettings>().await;
// Parse certificates
let now = now() as i64;
let mut certs_expired = Vec::new();
let mut certs_expirations = AHashMap::new();
for cert_obj in bp.list_infallible::<Certificate>().await {
let obj_id = cert_obj.id;
let revision = cert_obj.revision;
let mut cert = cert_obj.object;
let is_file_backed = matches!(cert.certificate, PublicText::File(_))
|| matches!(cert.private_key, SecretText::File(_));
let mut public = None;
let mut refreshed_meta = None;
if is_file_backed {
let pem = match cert.certificate.value().await {
Ok(value) => value.into_owned().into_bytes(),
Err(err) => {
bp.build_error(obj_id, format!("Failed to obtain certificate value: {err}"));
continue;
}
};
match ParsedCert::parse(&pem) {
Ok(parsed) => {
let not_valid_after =
UTCDateTime::from_timestamp(parsed.valid_not_after.timestamp());
let not_valid_before =
UTCDateTime::from_timestamp(parsed.valid_not_before.timestamp());
let sans = Map::new(parsed.sans);
if cert.not_valid_after != not_valid_after
|| cert.not_valid_before != not_valid_before
|| cert.issuer != parsed.issuer
|| cert.subject_alternative_names != sans
{
refreshed_meta =
Some((not_valid_after, not_valid_before, parsed.issuer, sans));
}
public = Some(pem);
}
Err(err) => {
bp.build_error(obj_id, format!("Invalid certificate: {err}"));
continue;
}
}
}
let (not_valid_after, not_valid_before) = match refreshed_meta.as_ref() {
Some((after, before, _, _)) => (after.timestamp(), before.timestamp()),
None => (
cert.not_valid_after.timestamp(),
cert.not_valid_before.timestamp(),
),
};
if not_valid_after <= now {
certs_expired.push((
obj_id,
cert.subject_alternative_names.clone().into_inner(),
Object {
inner: cert.into(),
revision,
},
));
continue;
} else if not_valid_before > now {
continue; // Skip certificates that are not yet valid
}
let secret = match cert.private_key.secret().await {
Ok(secret) => secret.into_owned().into_bytes(),
Err(err) => {
bp.build_error(
obj_id,
format!("Failed to obtain private key secret: {err}"),
);
continue;
}
};
let public = match public {
Some(public) => public,
None => match cert.certificate.value().await {
Ok(value) => value.into_owned().into_bytes(),
Err(err) => {
bp.build_error(obj_id, format!("Failed to obtain certificate value: {err}"));
continue;
}
},
};
if let Some((not_valid_after, not_valid_before, issuer, sans)) = refreshed_meta {
let old = Object {
inner: cert.clone().into(),
revision,
};
cert.not_valid_after = not_valid_after;
cert.not_valid_before = not_valid_before;
cert.issuer = issuer;
cert.subject_alternative_names = sans;
let new = Object {
inner: cert.clone().into(),
revision,
};
if let Err(err) = bp
.registry
.write(RegistryWrite::update(obj_id.id(), &new, &old))
.await
{
trc::error!(
err.details("Failed to refresh TLS certificate metadata in registry.")
.caused_by(trc::location!())
);
}
}
// Add default certificate
if system
.default_certificate_id
.as_ref()
.is_some_and(|id| *id == obj_id.id())
{
cert.subject_alternative_names
.push_unchecked("*".to_string());
}
// Ensure that the most up-to-date certificate is used
cert.subject_alternative_names.inner_mut().retain(|name| {
if certs_expirations
.get(name)
.is_none_or(|expires| *expires < not_valid_after)
{
certs_expirations.insert(name.clone(), not_valid_after);
true
} else {
false
}
});
match build_certified_key(public, secret) {
Ok(key) => {
// Add certificates
let key = Arc::new(key);
for name in cert.subject_alternative_names.into_inner() {
subject_names.insert(name.as_str().into());
certificates.insert(
name.strip_prefix("*.")
.map(Into::into)
.unwrap_or_else(|| name.into_boxed_str()),
key.clone(),
);
}
}
Err(err) => {
bp.build_error(obj_id, format!("Invalid certificate: {err}"));
}
}
}
// Remove expired certificates
if !certs_expired.is_empty() {
for (id, sans, object) in certs_expired {
if let Err(err) = bp
.registry
.write(RegistryWrite::delete_object(id, &object))
.await
{
trc::error!(
err.details("Failed to delete expired TLS certificate from registry.")
.caused_by(trc::location!())
);
} else {
trc::event!(
Tls(trc::TlsEvent::ExpiredCertificateRemoved),
Details = sans
);
}
}
}
}
pub(crate) fn build_certified_key(
cert: Vec<u8>,
pk_bytes: Vec<u8>,
) -> Result<CertifiedKey, String> {
let mut pk = None;
for item in read_all(&mut Cursor::new(pk_bytes)) {
match item.map_err(|err| format!("Failed to read private key PEM: {err}"))? {
Item::Pkcs8Key(key) => {
pk = Some(PrivateKeyDer::Pkcs8(key));
break;
}
Item::Pkcs1Key(key) => {
pk = Some(PrivateKeyDer::Pkcs1(key));
break;
}
Item::Sec1Key(key) => {
pk = Some(PrivateKeyDer::Sec1(key));
break;
}
_ => continue, // Skip certificates, DH params, etc.
}
}
let pk = pk.ok_or_else(|| "No private keys found.".to_string())?;
let cert = certs(&mut Cursor::new(cert))
.collect::<Result<Vec<_>, _>>()
.map_err(|err| format!("Failed to read certificates: {err}"))?;
if !cert.is_empty() {
Ok(CertifiedKey {
cert,
key: any_supported_type(&pk)
.map_err(|err| format!("Failed to sign certificate: {err}",))?,
ocsp: None,
})
} else {
Err("No certificates found.".to_string())
}
}
pub(crate) fn build_self_signed_cert(
domains: impl Into<Vec<String>>,
) -> Result<CertifiedKey, String> {
let domains = domains
.into()
.into_iter()
.map(|domain| {
if domain.is_ascii() {
domain
} else {
idna::domain_to_ascii(&domain).unwrap_or(domain)
}
})
.collect::<Vec<_>>();
let rcgen::CertifiedKey { cert, signing_key } = generate_simple_self_signed(domains)
.map_err(|err| format!("Failed to generate self-signed certificate: {err}",))?;
build_certified_key(
cert.pem().into_bytes(),
signing_key.serialize_pem().into_bytes(),
)
}