From 8caf3b9d5678361383c855ba1ab0a06dc0169d42 Mon Sep 17 00:00:00 2001 From: mdecimus Date: Thu, 19 Oct 2023 19:48:39 +0200 Subject: [PATCH] Remote lookups, directory query scheduler, config restructuring --- Cargo.lock | 1 + crates/directory/src/config.rs | 68 +- crates/directory/src/lib.rs | 11 +- crates/directory/src/scheduled.rs | 59 + crates/install/src/main.rs | 10 +- crates/jmap/src/services/housekeeper.rs | 120 +- crates/main/src/main.rs | 7 +- crates/smtp/src/config/auth.rs | 91 +- crates/smtp/src/config/mod.rs | 13 - crates/smtp/src/config/resolver.rs | 2 + crates/smtp/src/config/scripts.rs | 26 +- crates/smtp/src/core/mod.rs | 2 - crates/smtp/src/core/params.rs | 1 - crates/smtp/src/inbound/data.rs | 14 +- crates/smtp/src/inbound/ehlo.rs | 158 +- crates/smtp/src/inbound/mail.rs | 26 +- crates/smtp/src/inbound/spawn.rs | 1 - crates/smtp/src/scripts/plugins/lookup.rs | 265 +- crates/smtp/src/scripts/plugins/mod.rs | 4 +- crates/utils/Cargo.toml | 1 + crates/utils/src/config/cron.rs | 135 + crates/utils/src/config/mod.rs | 1 + crates/utils/src/listener/listen.rs | 7 +- resources/config/common/server.toml | 28 + .../config/{common.toml => common/tls.toml} | 43 +- resources/config/common/tracing.toml | 21 + resources/config/directory.toml | 181 - resources/config/directory/imap.toml | 27 + resources/config/directory/ldap.toml | 51 + resources/config/directory/lmtp.toml | 31 + resources/config/directory/memory.toml | 47 + resources/config/directory/sql.toml | 40 + resources/config/imap/listener.toml | 17 + .../config/{imap.toml => imap/settings.toml} | 16 +- resources/config/jmap.toml | 179 - resources/config/jmap/auth.toml | 10 + resources/config/jmap/listener.toml | 8 + resources/config/jmap/oauth.toml | 19 + resources/config/jmap/protocol.toml | 48 + resources/config/jmap/push.toml | 21 + resources/config/jmap/ratelimit.toml | 13 + resources/config/jmap/sieve.toml | 36 + resources/config/jmap/store.toml | 41 + resources/config/jmap/websockets.toml | 8 + resources/config/macros.toml | 8 + resources/config/smtp.toml | 395 -- resources/config/smtp/auth.toml | 28 + resources/config/smtp/listener.toml | 24 + resources/config/smtp/milter.toml | 26 + resources/config/smtp/queue.toml | 53 + resources/config/smtp/remote.toml | 19 + resources/config/smtp/report.toml | 57 + resources/config/smtp/resolver.toml | 23 + resources/config/smtp/session.toml | 102 + resources/config/smtp/sieve.toml | 35 + resources/config/smtp/signature.toml | 18 + resources/config/smtp/spamfilter.toml | 111 + .../config/spamfilter/maps/allow_dmarc.list | 786 ++++ .../config/spamfilter/maps/allow_domains.list | 829 ++++ .../spamfilter/maps/allow_phishing.list | 5 + .../spamfilter/maps/allow_spf_dkim.list | 238 + .../spamfilter/maps/domains_disposable.list | 427 ++ .../config/spamfilter/maps/domains_free.list | 4007 +++++++++++++++++ .../config/spamfilter/maps/mime_types.map | 129 + .../maps/scores.map} | 0 .../config/spamfilter/maps/spam_trap.list | 2 + .../config/spamfilter/maps/suffix_list.dat.gz | Bin 0 -> 80969 bytes .../spamfilter/maps/url_redirectors.list | 1076 +++++ .../scripts}/bayes_classify.sieve | 0 .../scripts}/bounce.sieve | 0 .../scripts}/composites.sieve | 0 .../scripts}/config.sieve | 0 .../{sieve => spamfilter/scripts}/date.sieve | 0 .../{sieve => spamfilter/scripts}/dmarc.sieve | 0 .../scripts}/epilogue.sieve | 30 - .../{sieve => spamfilter/scripts}/from.sieve | 0 .../scripts}/greylist.sieve | 0 .../scripts}/headers.sieve | 0 .../{sieve => spamfilter/scripts}/helo.sieve | 0 .../{sieve => spamfilter/scripts}/html.sieve | 0 .../{sieve => spamfilter/scripts}/ip.sieve | 0 .../scripts}/messageid.sieve | 0 .../{sieve => spamfilter/scripts}/mime.sieve | 0 .../scripts}/prelude.sieve | 0 .../{sieve => spamfilter/scripts}/pyzor.sieve | 0 .../{sieve => spamfilter/scripts}/rbl.sieve | 11 +- .../scripts}/received.sieve | 0 .../scripts}/recipient.sieve | 0 .../scripts}/replies_in.sieve | 0 .../scripts}/replies_out.sieve | 0 .../scripts}/replyto.sieve | 0 .../scripts}/reputation.sieve | 0 .../config/spamfilter/scripts/scores.sieve | 27 + .../scripts}/spamtrap.sieve | 0 .../scripts}/subject.sieve | 0 .../{sieve => spamfilter/scripts}/url.sieve | 9 +- tests/src/imap/mod.rs | 2 +- tests/src/jmap/mod.rs | 2 +- tests/src/smtp/inbound/antispam.rs | 24 +- tests/src/smtp/inbound/dnsrbl.rs | 164 - tests/src/smtp/inbound/mod.rs | 1 - tests/src/smtp/lookup/utils.rs | 25 +- tests/src/smtp/mod.rs | 9 +- tests/src/smtp/outbound/mod.rs | 24 +- 104 files changed, 9079 insertions(+), 1555 deletions(-) create mode 100644 crates/directory/src/scheduled.rs create mode 100644 crates/utils/src/config/cron.rs create mode 100644 resources/config/common/server.toml rename resources/config/{common.toml => common/tls.toml} (52%) create mode 100644 resources/config/common/tracing.toml delete mode 100644 resources/config/directory.toml create mode 100644 resources/config/directory/imap.toml create mode 100644 resources/config/directory/ldap.toml create mode 100644 resources/config/directory/lmtp.toml create mode 100644 resources/config/directory/memory.toml create mode 100644 resources/config/directory/sql.toml create mode 100644 resources/config/imap/listener.toml rename resources/config/{imap.toml => imap/settings.toml} (57%) delete mode 100644 resources/config/jmap.toml create mode 100644 resources/config/jmap/auth.toml create mode 100644 resources/config/jmap/listener.toml create mode 100644 resources/config/jmap/oauth.toml create mode 100644 resources/config/jmap/protocol.toml create mode 100644 resources/config/jmap/push.toml create mode 100644 resources/config/jmap/ratelimit.toml create mode 100644 resources/config/jmap/sieve.toml create mode 100644 resources/config/jmap/store.toml create mode 100644 resources/config/jmap/websockets.toml create mode 100644 resources/config/macros.toml delete mode 100644 resources/config/smtp.toml create mode 100644 resources/config/smtp/auth.toml create mode 100644 resources/config/smtp/listener.toml create mode 100644 resources/config/smtp/milter.toml create mode 100644 resources/config/smtp/queue.toml create mode 100644 resources/config/smtp/remote.toml create mode 100644 resources/config/smtp/report.toml create mode 100644 resources/config/smtp/resolver.toml create mode 100644 resources/config/smtp/session.toml create mode 100644 resources/config/smtp/sieve.toml create mode 100644 resources/config/smtp/signature.toml create mode 100644 resources/config/smtp/spamfilter.toml create mode 100644 resources/config/spamfilter/maps/allow_dmarc.list create mode 100644 resources/config/spamfilter/maps/allow_domains.list create mode 100644 resources/config/spamfilter/maps/allow_phishing.list create mode 100644 resources/config/spamfilter/maps/allow_spf_dkim.list create mode 100644 resources/config/spamfilter/maps/domains_disposable.list create mode 100644 resources/config/spamfilter/maps/domains_free.list create mode 100644 resources/config/spamfilter/maps/mime_types.map rename resources/config/{sieve/scores.sieve => spamfilter/maps/scores.map} (100%) create mode 100644 resources/config/spamfilter/maps/spam_trap.list create mode 100644 resources/config/spamfilter/maps/suffix_list.dat.gz create mode 100644 resources/config/spamfilter/maps/url_redirectors.list rename resources/config/{sieve => spamfilter/scripts}/bayes_classify.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/bounce.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/composites.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/config.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/date.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/dmarc.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/epilogue.sieve (57%) rename resources/config/{sieve => spamfilter/scripts}/from.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/greylist.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/headers.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/helo.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/html.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/ip.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/messageid.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/mime.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/prelude.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/pyzor.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/rbl.sieve (96%) rename resources/config/{sieve => spamfilter/scripts}/received.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/recipient.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/replies_in.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/replies_out.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/replyto.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/reputation.sieve (100%) create mode 100644 resources/config/spamfilter/scripts/scores.sieve rename resources/config/{sieve => spamfilter/scripts}/spamtrap.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/subject.sieve (100%) rename resources/config/{sieve => spamfilter/scripts}/url.sieve (90%) delete mode 100644 tests/src/smtp/inbound/dnsrbl.rs diff --git a/Cargo.lock b/Cargo.lock index 7f4000db..ca45aca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5896,6 +5896,7 @@ name = "utils" version = "0.3.10" dependencies = [ "ahash 0.8.3", + "chrono", "dashmap", "mail-auth", "mail-send", diff --git a/crates/directory/src/config.rs b/crates/directory/src/config.rs index efe216e6..4fd581f4 100644 --- a/crates/directory/src/config.rs +++ b/crates/directory/src/config.rs @@ -31,6 +31,7 @@ use std::{ time::Duration, }; use utils::config::{ + cron::SimpleCron, utils::{AsKey, ParseValue}, Config, }; @@ -39,8 +40,8 @@ use ahash::AHashMap; use crate::{ imap::ImapDirectory, ldap::LdapDirectory, memory::MemoryDirectory, smtp::SmtpDirectory, - sql::SqlDirectory, AddressMapping, DirectoryConfig, DirectoryOptions, Lookup, LookupList, - MatchType, + sql::SqlDirectory, AddressMapping, DirectoryConfig, DirectoryOptions, DirectorySchedule, + Lookup, LookupList, MatchType, }; pub trait ConfigDirectory { @@ -57,6 +58,7 @@ impl ConfigDirectory for Config { let mut config = DirectoryConfig { directories: AHashMap::new(), lookups: AHashMap::new(), + schedules: Vec::new(), }; for id in self.sub_keys("directory") { // Parse directory @@ -89,10 +91,31 @@ impl ConfigDirectory for Config { }), ); } + + // Parse schedules + if let Some(cron) = + self.property::(("directory", id, "schedule.frequency"))? + { + let mut query = Vec::new(); + for (_, value) in self.values(("directory", id, "schedule.query")) { + query.push(value.to_string()); + } + + if !query.is_empty() { + config.schedules.push(DirectorySchedule { + cron, + query, + directory: directory.clone(), + }) + } else { + tracing::warn!("No scheduled query specified for directory {id:?}"); + } + } } // Parse lookups for lookup_id in self.sub_keys(("directory", id, "lookup")) { + let key = ("directory", id, "lookup", lookup_id).as_key(); let lookup = if is_directory { Lookup::Directory { directory: directory.clone(), @@ -101,39 +124,22 @@ impl ConfigDirectory for Config { .to_string(), } } else { - let key = ("directory", id, "lookup", lookup_id).as_key(); - match self.property::((&key, "type"))? { + let lookup_type = self.property::((&key, "type"))?; + let format = LookupFormat { + lookup_type: lookup_type.unwrap_or(LookupType::List), + comment: self.value((&key, "comment")).map(|s| s.to_string()), + separator: self.value((&key, "separator")).map(|s| s.to_string()), + }; + + match lookup_type { Some(LookupType::Map) => Lookup::Map { - map: self.parse_lookup_list( - (&key, "values"), - LookupFormat { - lookup_type: LookupType::Map, - comment: self.value((&key, "comment")).map(|s| s.to_string()), - separator: self - .value((&key, "separator")) - .map(|s| s.to_string()), - }, - )?, + map: self.parse_lookup_list((&key, "values"), format)?, }, - Some(lookup_type) => Lookup::List { - list: self.parse_lookup_list( - (&key, "values"), - LookupFormat { - lookup_type, - comment: self.value((&key, "comment")).map(|s| s.to_string()), - separator: None, - }, - )?, + Some(_) => Lookup::List { + list: self.parse_lookup_list((&key, "values"), format)?, }, None => Lookup::List { - list: self.parse_lookup_list( - key, - LookupFormat { - lookup_type: LookupType::List, - comment: None, - separator: None, - }, - )?, + list: self.parse_lookup_list(key.as_str(), format)?, }, } }; diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs index 3c860c82..87902685 100644 --- a/crates/directory/src/lib.rs +++ b/crates/directory/src/lib.rs @@ -34,13 +34,14 @@ use ldap3::LdapError; use mail_send::Credentials; use sieve::runtime::{tests::glob::GlobPattern, Variable}; use smtp_proto::IntoString; -use utils::config::DynValue; +use utils::config::{cron::SimpleCron, DynValue}; pub mod cache; pub mod config; pub mod imap; pub mod ldap; pub mod memory; +pub mod scheduled; pub mod secret; pub mod smtp; pub mod sql; @@ -325,6 +326,14 @@ pub enum AddressMapping { pub struct DirectoryConfig { pub directories: AHashMap>, pub lookups: AHashMap>, + pub schedules: Vec, +} + +#[derive(Debug, Clone)] +pub struct DirectorySchedule { + pub cron: SimpleCron, + pub query: Vec, + pub directory: Arc, } pub type Result = std::result::Result; diff --git a/crates/directory/src/scheduled.rs b/crates/directory/src/scheduled.rs new file mode 100644 index 00000000..5029ca76 --- /dev/null +++ b/crates/directory/src/scheduled.rs @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Stalwart Labs Ltd. + * + * This file is part of Stalwart Mail Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * in the LICENSE file at the top-level directory of this distribution. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the AGPLv3 license by + * purchasing a commercial license. Please contact licensing@stalw.art + * for more details. +*/ + +use tokio::sync::watch; + +use crate::DirectorySchedule; + +impl DirectorySchedule { + pub fn spawn(self, mut shutdown_rx: watch::Receiver) { + tokio::spawn(async move { + loop { + if tokio::time::timeout(self.cron.time_to_next(), shutdown_rx.changed()) + .await + .is_ok() + { + tracing::debug!("Directory query scheduler task exiting."); + return; + } + + for query in &self.query { + if let Err(err) = self.directory.query(query, &[]).await { + tracing::warn!( + context = "directory-scheduler", + event = "error", + query = query, + reason = ?err, + ); + } else { + tracing::debug!( + context = "directory-scheduler", + event = "success", + query = query, + ); + } + } + } + }); + } +} diff --git a/crates/install/src/main.rs b/crates/install/src/main.rs index 53fd66ad..829eb9d0 100644 --- a/crates/install/src/main.rs +++ b/crates/install/src/main.rs @@ -36,11 +36,11 @@ use pwhash::sha512_crypt; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use rusqlite::{Connection, OpenFlags}; -const CFG_COMMON: &str = include_str!("../../../resources/config/common.toml"); -const CFG_DIRECTORY: &str = include_str!("../../../resources/config/directory.toml"); -const CFG_JMAP: &str = include_str!("../../../resources/config/jmap.toml"); -const CFG_IMAP: &str = include_str!("../../../resources/config/imap.toml"); -const CFG_SMTP: &str = include_str!("../../../resources/config/smtp.toml"); +const CFG_COMMON: &str = "../../../resources/config/common.toml"; +const CFG_DIRECTORY: &str = "../../../resources/config/directory_sql.toml"; +const CFG_JMAP: &str = "../../../resources/config/jmap.toml"; +const CFG_IMAP: &str = "../../../resources/config/imap.toml"; +const CFG_SMTP: &str = "../../../resources/config/smtp.toml"; #[cfg(target_os = "linux")] const SERVICE: &str = include_str!("../../../resources/systemd/stalwart-mail.service"); diff --git a/crates/jmap/src/services/housekeeper.rs b/crates/jmap/src/services/housekeeper.rs index ff23888a..9d271490 100644 --- a/crates/jmap/src/services/housekeeper.rs +++ b/crates/jmap/src/services/housekeeper.rs @@ -21,14 +21,14 @@ * for more details. */ -use std::{ - sync::Arc, - time::{Duration, Instant}, -}; +use std::{sync::Arc, time::Instant}; -use chrono::{Datelike, TimeZone, Timelike}; use tokio::sync::mpsc; -use utils::{config::Config, failed, map::ttl_dashmap::TtlMap, UnwrapFailure}; +use utils::{ + config::{cron::SimpleCron, Config}, + map::ttl_dashmap::TtlMap, + UnwrapFailure, +}; use crate::JMAP; @@ -41,29 +41,20 @@ pub enum Event { Exit, } -enum SimpleCron { - Day { hour: u32, minute: u32 }, - Week { day: u32, hour: u32, minute: u32 }, - Hour { minute: u32 }, -} - const TASK_PURGE_DB: usize = 0; const TASK_PURGE_BLOBS: usize = 1; const TASK_PURGE_SESSIONS: usize = 2; pub fn spawn_housekeeper(core: Arc, settings: &Config, mut rx: mpsc::Receiver) { - let purge_db_at = - SimpleCron::parse(settings.value("jmap.purge.schedule.db").unwrap_or("0 3 *")); - let purge_blobs_at = SimpleCron::parse( - settings - .value("jmap.purge.schedule.blobs") - .unwrap_or("30 3 *"), - ); - let purge_cache = SimpleCron::parse( - settings - .value("jmap.purge.schedule.sessions") - .unwrap_or("15 * *"), - ); + let purge_db_at = settings + .property_or_static::("jmap.purge.schedule.db", "0 3 *") + .failed("Initialize housekeeper"); + let purge_blobs_at = settings + .property_or_static::("jmap.purge.schedule.blobs", "30 3 *") + .failed("Initialize housekeeper"); + let purge_cache = settings + .property_or_static::("jmap.purge.schedule.sessions", "15 * *") + .failed("Initialize housekeeper"); tokio::spawn(async move { tracing::debug!("Housekeeper task started."); @@ -147,84 +138,3 @@ pub fn spawn_housekeeper(core: Arc, settings: &Config, mut rx: mpsc::Recei pub fn init_housekeeper() -> (mpsc::Sender, mpsc::Receiver) { mpsc::channel::(IPC_CHANNEL_BUFFER) } - -impl SimpleCron { - pub fn parse(value: &str) -> Self { - let mut hour = 0; - let mut minute = 0; - - for (pos, value) in value.split(' ').enumerate() { - if pos == 0 { - minute = value.parse::().failed("parse cron minute"); - if !(0..=59).contains(&minute) { - failed(&format!("parse minute, invalid value: {}", minute)); - } - } else if pos == 1 { - if value.as_bytes().first().failed("parse cron weekday") == &b'*' { - return SimpleCron::Hour { minute }; - } else { - hour = value.parse::().failed("parse cron hour"); - if !(0..=23).contains(&hour) { - failed(&format!("parse hour, invalid value: {}", hour)); - } - } - } else if pos == 2 { - if value.as_bytes().first().failed("parse cron weekday") == &b'*' { - return SimpleCron::Day { hour, minute }; - } else { - let day = value.parse::().failed("parse cron weekday"); - if !(1..=7).contains(&hour) { - failed(&format!( - "parse weekday, invalid value: {}, range is 1 (Monday) to 7 (Sunday).", - hour, - )); - } - - return SimpleCron::Week { day, hour, minute }; - } - } - } - - failed("parse cron expression."); - } - - pub fn time_to_next(&self) -> Duration { - let now = chrono::Local::now(); - let next = match self { - SimpleCron::Day { hour, minute } => { - let next = chrono::Local - .with_ymd_and_hms(now.year(), now.month(), now.day(), *hour, *minute, 0) - .unwrap(); - if next < now { - next + chrono::Duration::days(1) - } else { - next - } - } - SimpleCron::Week { day, hour, minute } => { - let next = chrono::Local - .with_ymd_and_hms(now.year(), now.month(), now.day(), *hour, *minute, 0) - .unwrap(); - if next < now { - next + chrono::Duration::days( - (7 - now.weekday().number_from_monday() + *day).into(), - ) - } else { - next - } - } - SimpleCron::Hour { minute } => { - let next = chrono::Local - .with_ymd_and_hms(now.year(), now.month(), now.day(), now.hour(), *minute, 0) - .unwrap(); - if next < now { - next + chrono::Duration::hours(1) - } else { - next - } - } - }; - - (next - now).to_std().unwrap() - } -} diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index ad7f69ea..df20ceae 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -73,7 +73,7 @@ async fn main() -> std::io::Result<()> { .failed("Invalid configuration file"); // Spawn servers - let shutdown_tx = servers.spawn(|server, shutdown_rx| { + let (shutdown_tx, shutdown_rx) = servers.spawn(|server, shutdown_rx| { match &server.protocol { ServerProtocol::Smtp | ServerProtocol::Lmtp => { server.spawn(SmtpSessionManager::new(smtp.clone()), shutdown_rx) @@ -95,6 +95,11 @@ async fn main() -> std::io::Result<()> { }; }); + // Spawn scheduled directory queries + for schedule in directory.schedules { + schedule.spawn(shutdown_rx.clone()); + } + // Wait for shutdown signal wait_for_shutdown(&format!( "Shutting down Stalwart Mail Server v{}...", diff --git a/crates/smtp/src/config/auth.rs b/crates/smtp/src/config/auth.rs index 65f770ea..d0ae9c50 100644 --- a/crates/smtp/src/config/auth.rs +++ b/crates/smtp/src/config/auth.rs @@ -35,14 +35,12 @@ use utils::config::{ use super::{ if_block::ConfigIf, ArcAuthConfig, ArcSealer, ConfigContext, DkimAuthConfig, - DkimCanonicalization, DkimSigner, DmarcAuthConfig, DnsBlConfig, EnvelopeKey, IfBlock, IfThen, - IpRevAuthConfig, MailAuthConfig, SpfAuthConfig, VerifyStrategy, DNSBL_EHLO, DNSBL_FROM, - DNSBL_IP, DNSBL_IPREV, DNSBL_RETURN_PATH, + DkimCanonicalization, DkimSigner, DmarcAuthConfig, EnvelopeKey, IfBlock, IpRevAuthConfig, + MailAuthConfig, SpfAuthConfig, VerifyStrategy, }; pub trait ConfigAuth { fn parse_mail_auth(&self, ctx: &ConfigContext) -> super::Result; - fn parse_dnsbl(&self, ctx: &ConfigContext) -> super::Result; fn parse_signatures(&self, ctx: &mut ConfigContext) -> super::Result<()>; } @@ -108,63 +106,6 @@ impl ConfigAuth for Config { .parse_if_block("auth.iprev.verify", ctx, &envelope_conn_keys)? .unwrap_or_else(|| IfBlock::new(VerifyStrategy::Relaxed)), }, - dnsbl: self.parse_dnsbl(ctx)?, - }) - } - - fn parse_dnsbl(&self, ctx: &ConfigContext) -> super::Result { - let verify = self - .parse_if_block::>( - "auth.dnsbl.verify", - ctx, - &[EnvelopeKey::RemoteIp, EnvelopeKey::Listener], - )? - .unwrap_or_default(); - - Ok(DnsBlConfig { - verify: IfBlock { - if_then: { - let mut if_then = Vec::with_capacity(verify.if_then.len()); - for cond in verify.if_then { - if_then.push(IfThen { - conditions: cond.conditions, - then: cond.then.into_dnsbl("auth.dnsbl.verify.if")?, - }); - } - if_then - }, - default: verify.default.into_dnsbl("auth.dnsbl.verify.else")?, - }, - ip_lookup: self - .values("auth.dnsbl.lookup.ip") - .filter_map(|(_, v)| { - if !v.is_empty() { - if !v.ends_with('.') { - format!("{v}.") - } else { - v.to_string() - } - .into() - } else { - None - } - }) - .collect(), - domain_lookup: self - .values("auth.dnsbl.lookup.domain") - .filter_map(|(_, v)| { - if !v.is_empty() { - if !v.ends_with('.') { - format!("{v}.") - } else { - v.to_string() - } - .into() - } else { - None - } - }) - .collect(), }) } @@ -400,31 +341,3 @@ impl Default for DkimCanonicalization { } } } - -trait IntoDnsbl { - fn into_dnsbl(self, key: impl AsKey) -> super::Result; -} - -impl IntoDnsbl for Vec { - fn into_dnsbl(self, key: impl AsKey) -> super::Result { - let mut dns_bl = 0; - for value in self { - dns_bl |= match value.as_str() { - "ip" => DNSBL_IP, - "iprev" => DNSBL_IPREV, - "ehlo" | "helo" => DNSBL_EHLO, - "return-path" | "sender" | "mail-from" => DNSBL_RETURN_PATH, - "from" => DNSBL_FROM, - _ => { - return Err(format!( - "Invalid DNSBL value {:?} for key {:?}.", - value, - key.as_key() - )) - } - }; - } - - Ok(dns_bl) - } -} diff --git a/crates/smtp/src/config/mod.rs b/crates/smtp/src/config/mod.rs index f39881eb..93a7944f 100644 --- a/crates/smtp/src/config/mod.rs +++ b/crates/smtp/src/config/mod.rs @@ -481,7 +481,6 @@ pub struct MailAuthConfig { pub spf: SpfAuthConfig, pub dmarc: DmarcAuthConfig, pub iprev: IpRevAuthConfig, - pub dnsbl: DnsBlConfig, } pub enum DkimSigner { @@ -516,18 +515,6 @@ pub struct IpRevAuthConfig { pub verify: IfBlock, } -pub struct DnsBlConfig { - pub verify: IfBlock, - pub ip_lookup: Vec, - pub domain_lookup: Vec, -} - -pub const DNSBL_IP: u32 = 1; -pub const DNSBL_IPREV: u32 = 1 << 1; -pub const DNSBL_EHLO: u32 = 1 << 2; -pub const DNSBL_RETURN_PATH: u32 = 1 << 3; -pub const DNSBL_FROM: u32 = 1 << 4; - #[derive(Debug, Clone)] pub struct DkimCanonicalization { pub headers: Canonicalization, diff --git a/crates/smtp/src/config/resolver.rs b/crates/smtp/src/config/resolver.rs index 4d2e03a9..2350de82 100644 --- a/crates/smtp/src/config/resolver.rs +++ b/crates/smtp/src/config/resolver.rs @@ -187,6 +187,8 @@ impl ConfigResolver for Config { if has_values { tracing::warn!("Failed to parse public suffixes from any source."); + } else { + tracing::warn!("No public suffixes list was specified."); } Ok(PublicSuffix::default()) diff --git a/crates/smtp/src/config/scripts.rs b/crates/smtp/src/config/scripts.rs index b318cb8a..f4bd610b 100644 --- a/crates/smtp/src/config/scripts.rs +++ b/crates/smtp/src/config/scripts.rs @@ -21,9 +21,14 @@ * for more details. */ -use std::time::Duration; +use std::{ + collections::HashSet, + time::{Duration, Instant}, +}; +use ahash::AHashMap; use nlp::bayes::cache::BayesTokenCache; +use parking_lot::RwLock; use sieve::{compiler::grammar::Capability, Compiler, Runtime}; use crate::{ @@ -45,6 +50,24 @@ pub trait ConfigSieve { pub struct SieveContext { pub psl: PublicSuffix, pub bayes_cache: BayesTokenCache, + pub remote_lists: RemoteLists, +} + +pub struct RemoteLists { + pub lists: RwLock>, +} + +pub struct RemoteList { + pub entries: HashSet, + pub expires: Instant, +} + +impl Default for RemoteLists { + fn default() -> Self { + Self { + lists: RwLock::new(AHashMap::new()), + } + } } impl ConfigSieve for Config { @@ -58,6 +81,7 @@ impl ConfigSieve for Config { self.property_or_static("bayes.cache.ttl.positive", "1h")?, self.property_or_static("bayes.cache.ttl.negative", "1h")?, ), + remote_lists: Default::default(), }; // Allocate compiler and runtime diff --git a/crates/smtp/src/core/mod.rs b/crates/smtp/src/core/mod.rs index 139d8d6d..b14017f5 100644 --- a/crates/smtp/src/core/mod.rs +++ b/crates/smtp/src/core/mod.rs @@ -241,7 +241,6 @@ pub struct SessionParameters { pub iprev: VerifyStrategy, pub spf_ehlo: VerifyStrategy, pub spf_mail_from: VerifyStrategy, - pub dnsbl_policy: u32, } impl SessionData { @@ -439,7 +438,6 @@ impl Session { spf_mail_from: crate::config::VerifyStrategy::Disable, can_expn: false, can_vrfy: false, - dnsbl_policy: 0, }, in_flight: vec![], } diff --git a/crates/smtp/src/core/params.rs b/crates/smtp/src/core/params.rs index f9bc1918..570b24d6 100644 --- a/crates/smtp/src/core/params.rs +++ b/crates/smtp/src/core/params.rs @@ -35,7 +35,6 @@ impl Session { self.params.spf_ehlo = *self.core.mail_auth.spf.verify_ehlo.eval(self).await; self.params.spf_mail_from = *self.core.mail_auth.spf.verify_mail_from.eval(self).await; self.params.iprev = *self.core.mail_auth.iprev.verify.eval(self).await; - self.params.dnsbl_policy = *self.core.mail_auth.dnsbl.verify.eval(self).await; // Ehlo parameters let ec = &self.core.session.config.ehlo; diff --git a/crates/smtp/src/inbound/data.rs b/crates/smtp/src/inbound/data.rs index 11afb640..64674182 100644 --- a/crates/smtp/src/inbound/data.rs +++ b/crates/smtp/src/inbound/data.rs @@ -44,9 +44,8 @@ use tokio::{ }; use crate::{ - config::DNSBL_FROM, core::{Session, SessionAddress, State}, - queue::{self, DomainPart, Message, SimpleEnvelope}, + queue::{self, Message, SimpleEnvelope}, reporting::analysis::AnalyzeReport, scripts::ScriptResult, }; @@ -68,17 +67,6 @@ impl Session { return (&b"550 5.7.7 Failed to parse message.\r\n"[..]).into(); }; - // Validate DNSBL - let from = auth_message.from(); - let from_domain = from.domain_part(); - if !from_domain.is_empty() - && !self - .is_domain_dnsbl_allowed(from_domain, "from", DNSBL_FROM) - .await - { - return self.reset_dnsbl_error().unwrap().into(); - } - // Loop detection let dc = &self.core.session.config.data; let ac = &self.core.mail_auth; diff --git a/crates/smtp/src/inbound/ehlo.rs b/crates/smtp/src/inbound/ehlo.rs index f36d7d68..e329a92e 100644 --- a/crates/smtp/src/inbound/ehlo.rs +++ b/crates/smtp/src/inbound/ehlo.rs @@ -21,13 +21,9 @@ * for more details. */ -use std::{net::IpAddr, time::SystemTime}; +use std::time::SystemTime; -use crate::{ - config::{DNSBL_EHLO, DNSBL_IP}, - core::Session, - scripts::ScriptResult, -}; +use crate::{core::Session, scripts::ScriptResult}; use mail_auth::spf::verify::HasLabels; use smtp_proto::*; use tokio::io::{AsyncRead, AsyncWrite}; @@ -51,16 +47,6 @@ impl Session { return self.write(b"550 5.5.0 Invalid EHLO domain.\r\n").await; } - // Check DNSBL - if !self - .is_domain_dnsbl_allowed(&domain, "ehlo", DNSBL_EHLO) - .await - { - self.write_dnsbl_error().await?; - self.reset_dnsbl_error(); // Reset error in case a new EHLO is issued - return Ok(()); - } - // SPF check let prev_helo_domain = std::mem::replace(&mut self.data.helo_domain, domain); if self.params.spf_ehlo.verify() { @@ -223,144 +209,4 @@ impl Session { response.write(&mut buf).ok(); self.write(&buf).await } - - pub async fn is_domain_dnsbl_allowed( - &mut self, - domain: &str, - context: &str, - policy_type: u32, - ) -> bool { - let domain_ = domain.to_lowercase(); - let is_fqdn = domain.ends_with('.'); - if (self.params.dnsbl_policy & policy_type) != 0 { - for dnsbl in &self.core.mail_auth.dnsbl.domain_lookup { - if self - .is_dns_blocked(if is_fqdn { - format!("{domain_}{dnsbl}") - } else { - format!("{domain_}.{dnsbl}") - }) - .await - { - tracing::info!(parent: &self.span, - context = context, - event = "reject", - reason = "dnsbl", - list = dnsbl, - domain = domain, - ); - self.data.dnsbl_error = format!( - "554 5.7.1 Service unavailable; Domain '{domain}' blocked using {dnsbl}\r\n" - ) - .into_bytes() - .into(); - return false; - } - } - } - true - } - - pub async fn verify_ip_dnsbl(&mut self) -> bool { - if (self.params.dnsbl_policy & DNSBL_IP) != 0 { - for dnsbl in &self.core.mail_auth.dnsbl.ip_lookup { - if self - .is_dns_blocked(self.data.remote_ip.to_dnsbl(dnsbl)) - .await - { - tracing::info!(parent: &self.span, - context = "connect", - event = "reject", - reason = "dnsbl", - list = dnsbl, - ip = self.data.remote_ip.to_string(), - ); - self.data.dnsbl_error = format!( - "554 5.7.1 Service unavailable; IP address {} blocked using {}\r\n", - self.data.remote_ip, dnsbl - ) - .into_bytes() - .into(); - return false; - } - } - } - true - } - - async fn is_dns_blocked(&self, domain: String) -> bool { - match self.core.resolvers.dns.ipv4_lookup(&domain).await { - Ok(ips) => { - for ip in ips.iter() { - if ip.octets()[0..2] == [127, 0] { - return true; - } - } - tracing::debug!(parent: &self.span, - context = "dnsbl", - event = "invalid-reply", - query = domain, - reply = ?ips, - ); - } - Err(mail_auth::Error::DnsRecordNotFound(_)) => (), - Err(err) => { - tracing::debug!(parent: &self.span, - context = "dnsbl", - event = "dnserror", - query = domain, - reson = %err, - ); - } - } - false - } - - pub async fn write_dnsbl_error(&mut self) -> Result<(), ()> { - if let Some(error) = &self.data.dnsbl_error { - self.write(&error.to_vec()).await - } else { - Ok(()) - } - } - - pub fn has_dnsbl_error(&mut self) -> bool { - self.data.dnsbl_error.is_some() - } - - pub fn reset_dnsbl_error(&mut self) -> Option> { - self.data.dnsbl_error.take() - } -} - -pub trait ToDnsbl { - fn to_dnsbl(&self, host: &str) -> String; -} - -impl ToDnsbl for IpAddr { - fn to_dnsbl(&self, dnsbl: &str) -> String { - use std::fmt::Write; - - match self { - IpAddr::V4(ip) => { - let mut host = String::with_capacity(dnsbl.len() + 16); - for octet in ip.octets().iter().rev() { - let _ = write!(host, "{octet}."); - } - host.push_str(dnsbl); - host - } - IpAddr::V6(ip) => { - let mut host = Vec::with_capacity(dnsbl.len() + 64); - for segment in ip.segments().iter().rev() { - for &p in format!("{segment:04x}").as_bytes().iter().rev() { - host.push(p); - host.push(b'.'); - } - } - host.extend_from_slice(dnsbl.as_bytes()); - String::from_utf8(host).unwrap_or_default() - } - } - } } diff --git a/crates/smtp/src/inbound/mail.rs b/crates/smtp/src/inbound/mail.rs index ed52d5c8..5ae17dce 100644 --- a/crates/smtp/src/inbound/mail.rs +++ b/crates/smtp/src/inbound/mail.rs @@ -28,7 +28,6 @@ use smtp_proto::{MailFrom, MAIL_BY_NOTIFY, MAIL_BY_RETURN, MAIL_REQUIRETLS}; use tokio::io::{AsyncRead, AsyncWrite}; use crate::{ - config::{DNSBL_IPREV, DNSBL_RETURN_PATH}, core::{Session, SessionAddress}, queue::DomainPart, scripts::ScriptResult, @@ -54,12 +53,7 @@ impl Session { return self .write(b"503 5.5.1 You must authenticate first.\r\n") .await; - } else if self.has_dnsbl_error() { - // There was a previous DNSBL error - return self.write_dnsbl_error().await; - } else if self.data.iprev.is_none() - && (self.params.iprev.verify() || (self.params.dnsbl_policy & DNSBL_IPREV) != 0) - { + } else if self.data.iprev.is_none() && self.params.iprev.verify() { let iprev = self .core .resolvers @@ -74,13 +68,6 @@ impl Session { ptr = iprev.ptr.as_ref().and_then(|p| p.first()).map(|p| p.as_str()).unwrap_or_default() ); - // Validate reverse hostname against DNSBL - if let Some(ptr) = iprev.ptr.as_ref().and_then(|l| l.first()) { - if !self.is_domain_dnsbl_allowed(ptr, "ptr", DNSBL_IPREV).await { - return self.write_dnsbl_error().await; - } - } - self.data.iprev = iprev.into(); } @@ -117,17 +104,6 @@ impl Session { (String::new(), String::new(), String::new()) }; - // Validate domain against DNSBL - if !domain.is_empty() - && !self - .is_domain_dnsbl_allowed(&domain, "mail-from", DNSBL_RETURN_PATH) - .await - { - self.write_dnsbl_error().await?; - self.reset_dnsbl_error(); // Reset error in case a new MAIL-FROM is issued later - return Ok(()); - } - let has_dsn = from.env_id.is_some(); self.data.mail_from = SessionAddress { address, diff --git a/crates/smtp/src/inbound/spawn.rs b/crates/smtp/src/inbound/spawn.rs index 84635f7a..ef4a3a6a 100644 --- a/crates/smtp/src/inbound/spawn.rs +++ b/crates/smtp/src/inbound/spawn.rs @@ -113,7 +113,6 @@ impl Session> { impl Session { pub async fn init_conn(&mut self) -> bool { self.eval_session_params().await; - self.verify_ip_dnsbl().await; // Sieve filtering if let Some(script) = self.core.session.config.connect.script.eval(self).await { diff --git a/crates/smtp/src/scripts/plugins/lookup.rs b/crates/smtp/src/scripts/plugins/lookup.rs index 8cd698b7..618887b8 100644 --- a/crates/smtp/src/scripts/plugins/lookup.rs +++ b/crates/smtp/src/scripts/plugins/lookup.rs @@ -21,10 +21,20 @@ * for more details. */ +use std::{ + collections::HashSet, + io::{BufRead, BufReader}, + time::{Duration, Instant}, +}; + use directory::DatabaseColumn; +use mail_auth::flate2; use sieve::{runtime::Variable, FunctionMap}; -use crate::config::scripts::SieveContext; +use crate::{ + config::scripts::{RemoteList, SieveContext}, + USER_AGENT, +}; use super::PluginContext; @@ -36,8 +46,8 @@ pub fn register_map(plugin_id: u32, fnc_map: &mut FunctionMap) { fnc_map.set_external_function("lookup_map", plugin_id, 2); } -pub fn register_map_many(plugin_id: u32, fnc_map: &mut FunctionMap) { - fnc_map.set_external_function("lookup_map_many", plugin_id, 2); +pub fn register_remote(plugin_id: u32, fnc_map: &mut FunctionMap) { + fnc_map.set_external_function("lookup_remote", plugin_id, 3); } pub fn register_local_domain(plugin_id: u32, fnc_map: &mut FunctionMap) { @@ -104,40 +114,225 @@ pub fn exec_map(ctx: PluginContext<'_>) -> Variable { Variable::default() } -pub fn exec_map_many(ctx: PluginContext<'_>) -> Variable { - let lookup_id = ctx.arguments[0].to_string(); - let items = match &ctx.arguments[1] { - Variable::Array(l) => l - .iter() - .filter_map(|i| { - if !i.is_empty() { - DatabaseColumn::from(i).into() - } else { - None - } - }) - .collect(), - v if !v.is_empty() => vec![DatabaseColumn::from(v)], - _ => vec![], - }; - let span = ctx.span; +pub fn exec_remote(ctx: PluginContext<'_>) -> Variable { + let resource = ctx.arguments[0].to_string(); + let item = ctx.arguments[1].to_string(); - if let Some(lookup) = ctx.core.sieve.lookup.get(lookup_id.as_ref()) { - items - .into_iter() - .filter_map(|item| ctx.handle.block_on(lookup.lookup(&[item]))) - .collect::>() - .into() - } else { - tracing::warn!( - parent: span, - context = "sieve:lookup", - event = "failed", - reason = "Unknown lookup id", - lookup_id = %lookup_id, - ); - Variable::default() + #[cfg(feature = "test_mode")] + { + if (resource.contains("open") && item.contains("open")) + || (resource.contains("tank") && item.contains("tank")) + { + return true.into(); + } } + + if resource.is_empty() || item.is_empty() { + return false.into(); + } + + const TIMEOUT: Duration = Duration::from_secs(45); + const RETRY: Duration = Duration::from_secs(3600); + const MAX_ENTRY_SIZE: usize = 256; + const MAX_ENTRIES: usize = 100000; + + match ctx + .core + .sieve + .runtime + .context() + .remote_lists + .lists + .read() + .get(resource.as_ref()) + { + Some(remote_list) if remote_list.expires < Instant::now() => { + return remote_list.entries.contains(item.as_ref()).into() + } + _ => {} + } + + enum Format { + List, + Csv { + column: u32, + separator: char, + skip_first: bool, + }, + } + + // Obtain parameters + let mut format = Format::List; + let mut expires = Duration::from_secs(12 * 3600); + + if let Some(arr) = ctx.arguments[2].as_array() { + // Obtain expiration + match arr.get(0) { + Some(Variable::Integer(v)) if *v > 0 => { + expires = Duration::from_secs(*v as u64); + } + Some(Variable::Float(v)) if *v > 0.0 => { + expires = Duration::from_secs(*v as u64); + } + _ => (), + } + + // Obtain list type + if matches!(arr.get(1), Some(Variable::String(list_type)) if list_type.eq_ignore_ascii_case("csv")) + { + format = Format::Csv { + column: arr.get(2).map(|v| v.to_integer()).unwrap_or_default() as u32, + separator: arr + .get(3) + .and_then(|v| v.to_string().chars().next()) + .unwrap_or(','), + skip_first: arr.get(4).map_or(false, |v| v.to_bool()), + }; + } + } + + // Lock remote list for writing + let mut _lock = ctx.core.sieve.runtime.context().remote_lists.lists.write(); + let list = _lock + .entry(resource.to_string()) + .or_insert_with(|| RemoteList { + entries: HashSet::new(), + expires: Instant::now(), + }); + + // Make sure that the list is still expired + if list.expires > Instant::now() { + return list.entries.contains(item.as_ref()).into(); + } + + let _enter = ctx.handle.enter(); + match ctx + .handle + .block_on( + reqwest::Client::builder() + .timeout(TIMEOUT) + .user_agent(USER_AGENT) + .build() + .unwrap_or_default() + .get(resource.as_ref()) + .send(), + ) + .and_then(|r| { + if r.status().is_success() { + ctx.handle.block_on(r.bytes()).map(Ok) + } else { + Ok(Err(r)) + } + }) { + Ok(Ok(bytes)) => { + let reader: Box = if resource.ends_with(".gz") { + Box::new(flate2::read::GzDecoder::new(&bytes[..])) + } else { + Box::new(&bytes[..]) + }; + + for (pos, line) in BufReader::new(reader).lines().enumerate() { + match line { + Ok(line_) => { + // Clear list once the first entry has been successfully fetched, decompressed and UTF8-decoded + if pos == 0 { + list.entries.clear(); + } + + match &format { + Format::List => { + let line = line_.trim(); + if !line.is_empty() { + list.entries.insert(line.to_string()); + } + } + Format::Csv { + column, + separator, + skip_first, + } if pos > 0 || !*skip_first => { + let mut in_quote = false; + let mut col_num = 0; + let mut entry = String::new(); + + for ch in line_.chars() { + if ch != '"' { + if ch == *separator && !in_quote { + if col_num == *column { + break; + } else { + col_num += 1; + } + } else if col_num == *column { + entry.push(ch); + if entry.len() > MAX_ENTRY_SIZE { + break; + } + } + } else { + in_quote = !in_quote; + } + } + + if !entry.is_empty() { + list.entries.insert(entry); + } + } + _ => (), + } + } + Err(err) => { + tracing::warn!( + parent: ctx.span, + context = "sieve:lookup_remote", + event = "failed", + resource = resource.as_ref(), + reason = %err, + ); + break; + } + } + + if list.entries.len() == MAX_ENTRIES { + break; + } + } + + tracing::debug!( + parent: ctx.span, + context = "sieve:lookup_remote", + event = "fetch", + resource = resource.as_ref(), + num_entries = list.entries.len(), + ); + + // Update expiration + list.expires = Instant::now() + expires; + return list.entries.contains(item.as_ref()).into(); + } + Ok(Err(response)) => { + tracing::warn!( + parent: ctx.span, + context = "sieve:lookup_remote", + event = "failed", + resource = resource.as_ref(), + status = %response.status(), + ); + } + Err(err) => { + tracing::warn!( + parent: ctx.span, + context = "sieve:lookup_remote", + event = "failed", + resource = resource.as_ref(), + reason = %err, + ); + } + } + + // Something went wrong, try again in one hour + list.expires = Instant::now() + RETRY; + false.into() } pub fn exec_local_domain(ctx: PluginContext<'_>) -> Variable { diff --git a/crates/smtp/src/scripts/plugins/mod.rs b/crates/smtp/src/scripts/plugins/mod.rs index c6d5ec7b..778907e0 100644 --- a/crates/smtp/src/scripts/plugins/mod.rs +++ b/crates/smtp/src/scripts/plugins/mod.rs @@ -51,7 +51,7 @@ const PLUGINS_EXEC: [ExecPluginFnc; 14] = [ exec::exec, lookup::exec, lookup::exec_map, - lookup::exec_map_many, + lookup::exec_remote, lookup::exec_local_domain, dns::exec, dns::exec_exists, @@ -67,7 +67,7 @@ const PLUGINS_REGISTER: [RegisterPluginFnc; 14] = [ exec::register, lookup::register, lookup::register_map, - lookup::register_map_many, + lookup::register_remote, lookup::register_local_domain, dns::register, dns::register_exists, diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 63ae2a8c..f3ab38df 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -22,6 +22,7 @@ opentelemetry-otlp = { version = "0.13.0", features = ["http-proto", "reqwest-cl opentelemetry-semantic-conventions = { version = "0.12.0" } dashmap = "5.4" ahash = { version = "0.8" } +chrono = "0.4" [target.'cfg(unix)'.dependencies] privdrop = "0.5.3" diff --git a/crates/utils/src/config/cron.rs b/crates/utils/src/config/cron.rs new file mode 100644 index 00000000..31a581af --- /dev/null +++ b/crates/utils/src/config/cron.rs @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Stalwart Labs Ltd. + * + * This file is part of Stalwart Mail Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * in the LICENSE file at the top-level directory of this distribution. + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * You can be released from the requirements of the AGPLv3 license by + * purchasing a commercial license. Please contact licensing@stalw.art + * for more details. +*/ + +use std::time::Duration; + +use chrono::{Datelike, TimeZone, Timelike}; + +use super::utils::ParseValue; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SimpleCron { + Day { hour: u32, minute: u32 }, + Week { day: u32, hour: u32, minute: u32 }, + Hour { minute: u32 }, +} + +impl SimpleCron { + pub fn time_to_next(&self) -> Duration { + let now = chrono::Local::now(); + let next = match self { + SimpleCron::Day { hour, minute } => { + let next = chrono::Local + .with_ymd_and_hms(now.year(), now.month(), now.day(), *hour, *minute, 0) + .unwrap(); + if next < now { + next + chrono::Duration::days(1) + } else { + next + } + } + SimpleCron::Week { day, hour, minute } => { + let next = chrono::Local + .with_ymd_and_hms(now.year(), now.month(), now.day(), *hour, *minute, 0) + .unwrap(); + if next < now { + next + chrono::Duration::days( + (7 - now.weekday().number_from_monday() + *day).into(), + ) + } else { + next + } + } + SimpleCron::Hour { minute } => { + let next = chrono::Local + .with_ymd_and_hms(now.year(), now.month(), now.day(), now.hour(), *minute, 0) + .unwrap(); + if next < now { + next + chrono::Duration::hours(1) + } else { + next + } + } + }; + + (next - now).to_std().unwrap() + } +} + +impl ParseValue for SimpleCron { + fn parse_value(key: impl super::utils::AsKey, value: &str) -> super::Result { + let mut hour = 0; + let mut minute = 0; + let key = key.as_key(); + + for (pos, value) in value.split(' ').enumerate() { + if pos == 0 { + minute = value.parse::().map_err(|_| { + format!("Invalid cron key {key:?}: failed to parse cron minute") + })?; + if !(0..=59).contains(&minute) { + return Err(format!( + "Invalid cron key {key:?}: failed to parse minute, invalid value: {minute}" + )); + } + } else if pos == 1 { + if value.as_bytes().first().ok_or_else(|| { + format!("Invalid cron key {key:?}: failed to parse cron weekday") + })? == &b'*' + { + return Ok(SimpleCron::Hour { minute }); + } else { + hour = value.parse::().map_err(|_| { + format!("Invalid cron key {key:?}: failed to parse cron hour") + })?; + if !(0..=23).contains(&hour) { + return Err(format!( + "Invalid cron key {key:?}: failed to parse hour, invalid value: {hour}" + )); + } + } + } else if pos == 2 { + if value.as_bytes().first().ok_or_else(|| { + format!("Invalid cron key {key:?}: failed to parse cron weekday") + })? == &b'*' + { + return Ok(SimpleCron::Day { hour, minute }); + } else { + let day = value.parse::().map_err(|_| { + format!("Invalid cron key {key:?}: failed to parse cron weekday") + })?; + if !(1..=7).contains(&hour) { + return Err(format!( + "Invalid cron key {key:?}: failed to parse weekday, invalid value: {}, range is 1 (Monday) to 7 (Sunday).", + hour, + )); + } + + return Ok(SimpleCron::Week { day, hour, minute }); + } + } + } + + Err(format!("Invalid cron key {key:?}: parse cron expression.")) + } +} diff --git a/crates/utils/src/config/mod.rs b/crates/utils/src/config/mod.rs index e71d46e5..7e2076a0 100644 --- a/crates/utils/src/config/mod.rs +++ b/crates/utils/src/config/mod.rs @@ -22,6 +22,7 @@ */ pub mod certificate; +pub mod cron; pub mod dynvalue; pub mod listener; pub mod parser; diff --git a/crates/utils/src/listener/listen.rs b/crates/utils/src/listener/listen.rs index bfff3be9..583f28f0 100644 --- a/crates/utils/src/listener/listen.rs +++ b/crates/utils/src/listener/listen.rs @@ -212,14 +212,17 @@ impl Servers { } } - pub fn spawn(self, spawn: impl Fn(Server, watch::Receiver)) -> watch::Sender { + pub fn spawn( + self, + spawn: impl Fn(Server, watch::Receiver), + ) -> (watch::Sender, watch::Receiver) { // Spawn listeners let (shutdown_tx, shutdown_rx) = watch::channel(false); for server in self.inner { spawn(server, shutdown_rx.clone()); } - shutdown_tx + (shutdown_tx, shutdown_rx) } } diff --git a/resources/config/common/server.toml b/resources/config/common/server.toml new file mode 100644 index 00000000..0654b83a --- /dev/null +++ b/resources/config/common/server.toml @@ -0,0 +1,28 @@ +############################################# +# Server configuration +############################################# + +[server] +hostname = "__HOST__" +max-connections = 8192 + +[server.run-as] +user = "stalwart-mail" +group = "stalwart-mail" + +[server.socket] +nodelay = true +reuse-addr = true +#reuse-port = true +backlog = 1024 +#ttl = 3600 +#send-buffer-size = 65535 +#recv-buffer-size = 65535 +#linger = 1 +#tos = 1 + +[global] +shared-map = {shard = 32, capacity = 10} +#thread-pool = 8 + + diff --git a/resources/config/common.toml b/resources/config/common/tls.toml similarity index 52% rename from resources/config/common.toml rename to resources/config/common/tls.toml index 148aa24b..4a325dc1 100644 --- a/resources/config/common.toml +++ b/resources/config/common/tls.toml @@ -1,15 +1,7 @@ ############################################# -# Server configuration +# TLS default configuration ############################################# -[server] -hostname = "__HOST__" -max-connections = 8192 - -[server.run-as] -user = "stalwart-mail" -group = "stalwart-mail" - [server.tls] enable = true implicit = false @@ -24,39 +16,6 @@ certificate = "default" # "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"] ignore-client-order = true -[server.socket] -nodelay = true -reuse-addr = true -#reuse-port = true -backlog = 1024 -#ttl = 3600 -#send-buffer-size = 65535 -#recv-buffer-size = 65535 -#linger = 1 -#tos = 1 - -[global] -shared-map = {shard = 32, capacity = 10} -#thread-pool = 8 - -#[global.tracing] -#method = "stdout" -#level = "trace" - -#[global.tracing] -#method = "open-telemetry" -#transport = "http" -#endpoint = "https://127.0.0.1/otel" -#headers = ["Authorization: "] -#level = "debug" - -[global.tracing] -method = "log" -path = "__PATH__/logs" -prefix = "stalwart.log" -rotate = "daily" -level = "info" - [certificate."default"] cert = "file://__CERT_PATH__" private-key = "file://__PK_PATH__" diff --git a/resources/config/common/tracing.toml b/resources/config/common/tracing.toml new file mode 100644 index 00000000..d2eb07dd --- /dev/null +++ b/resources/config/common/tracing.toml @@ -0,0 +1,21 @@ +############################################# +# Tracing & logging configuration +############################################# + +#[global.tracing] +#method = "stdout" +#level = "trace" + +#[global.tracing] +#method = "open-telemetry" +#transport = "http" +#endpoint = "https://127.0.0.1/otel" +#headers = ["Authorization: "] +#level = "debug" + +[global.tracing] +method = "log" +path = "__PATH__/logs" +prefix = "stalwart.log" +rotate = "daily" +level = "info" diff --git a/resources/config/directory.toml b/resources/config/directory.toml deleted file mode 100644 index 2899204e..00000000 --- a/resources/config/directory.toml +++ /dev/null @@ -1,181 +0,0 @@ -############################################# -# Directory configuration -############################################# - -[directory."sql"] -type = "sql" -address = "sqlite://__PATH__/data/accounts.sqlite3?mode=rwc" - -[directory."sql".options] -catch-all = true -#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } -subaddressing = true -#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } -superuser-group = "superusers" - -[directory."sql".pool] -max-connections = 10 -min-connections = 0 -#idle-timeout = "10m" - -[directory."sql".cache] -entries = 500 -ttl = {positive = '1h', negative = '10m'} - -[directory."sql".query] -name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = ? AND active = true" -members = "SELECT member_of FROM group_members WHERE name = ?" -recipients = "SELECT name FROM emails WHERE address = ?" -emails = "SELECT address FROM emails WHERE name = ? AND type != 'list' ORDER BY type DESC, address ASC" -verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type = 'primary' ORDER BY address LIMIT 5" -expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50" -domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1" - -[directory."sql".columns] -name = "name" -description = "description" -secret = "secret" -email = "address" -quota = "quota" -type = "type" - -[directory."ldap"] -type = "ldap" -address = "ldap://localhost:3893" -base-dn = "dc=example,dc=org" - -[directory."ldap".bind] -dn = "cn=serviceuser,ou=svcaccts,dc=example,dc=org" -secret = "mysecret" - -[directory."ldap".cache] -entries = 500 -ttl = {positive = '1h', negative = '10m'} - -[directory."ldap".options] -catch-all = true -#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } -subaddressing = true -#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } -superuser-group = "superusers" - -[directory."ldap".pool] -max-connections = 10 -min-connections = 0 -max-lifetime = "30m" -idle-timeout = "10m" -connect-timeout = "30s" - -[directory."ldap".filter] -name = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(uid=?))" -email = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=?)(mailAlias=?)(mailList=?)))" -verify = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*?*)(mailAlias=*?*)))" -expand = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(mailList=?))" -domains = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*@?)(mailAlias=*@?)))" - -[directory."ldap".object-classes] -user = "posixAccount" -group = "posixGroup" - -[directory."ldap".attributes] -name = "uid" -description = ["principalName", "description"] -secret = "userPassword" -groups = ["memberOf", "otherGroups"] -email = "mail" -email-alias = "mailAlias" -quota = "diskQuota" - -[directory."imap"] -type = "imap" -address = "127.0.0.1" -port = 993 - -[directory."imap".pool] -max-connections = 10 -min-connections = 0 -max-lifetime = "30m" -idle-timeout = "10m" -connect-timeout = "30s" - -[directory."imap".tls] -implicit = true -allow-invalid-certs = true - -[directory."imap".cache] -entries = 500 -ttl = {positive = '1h', negative = '10m'} - -[directory."imap".lookup] -domains = ["__DOMAIN__"] - -[directory."lmtp"] -type = "lmtp" -address = "127.0.0.1" -port = 11200 - -[directory."lmtp".limits] -auth-errors = 3 -rcpt = 5 - -[directory."lmtp".pool] -max-connections = 10 -min-connections = 0 -max-lifetime = "30m" -idle-timeout = "10m" -connect-timeout = "30s" - -[directory."lmtp".tls] -implicit = false -allow-invalid-certs = true - -[directory."lmtp".cache] -entries = 500 -ttl = {positive = '1h', negative = '10m'} - -[directory."lmtp".lookup] -domains = ["__DOMAIN__"] - -[directory."memory"] -type = "memory" - -[directory."memory".options] -catch-all = true -#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } -subaddressing = true -#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } -superuser-group = "superusers" - -[[directory."memory".users]] -name = "admin" -description = "Superuser" -secret = "changeme" -email = ["postmaster@__DOMAIN__"] -member-of = ["superusers"] - -[[directory."memory".users]] -name = "jane" -description = "Jane Doe" -secret = "abcde" -email = ["jane@__DOMAIN__", "jane.doe@__DOMAIN__"] -email-list = ["info@__DOMAIN__"] -member-of = ["sales", "support"] - -[[directory."memory".users]] -name = "bill" -description = "Bill Foobar" -secret = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe" -quota = 50000000 -email = ["bill@__DOMAIN__", "bill.foobar@__DOMAIN__"] -email-list = ["info@__DOMAIN__"] - -[[directory."memory".groups]] -name = "sales" -description = "Sales Team" - -[[directory."memory".groups]] -name = "support" -description = "Support Team" - -[directory."memory".lookup] -domains = ["__DOMAIN__"] diff --git a/resources/config/directory/imap.toml b/resources/config/directory/imap.toml new file mode 100644 index 00000000..a7441c4b --- /dev/null +++ b/resources/config/directory/imap.toml @@ -0,0 +1,27 @@ +############################################# +# Directory configuration +############################################# + +[directory."imap"] +type = "imap" +address = "127.0.0.1" +port = 993 + +[directory."imap".pool] +max-connections = 10 +min-connections = 0 +max-lifetime = "30m" +idle-timeout = "10m" +connect-timeout = "30s" + +[directory."imap".tls] +implicit = true +allow-invalid-certs = true + +[directory."imap".cache] +entries = 500 +ttl = {positive = '1h', negative = '10m'} + +[directory."imap".lookup] +domains = ["__DOMAIN__"] + diff --git a/resources/config/directory/ldap.toml b/resources/config/directory/ldap.toml new file mode 100644 index 00000000..63bf5fe3 --- /dev/null +++ b/resources/config/directory/ldap.toml @@ -0,0 +1,51 @@ +############################################# +# Directory configuration +############################################# + +[directory."default"] +type = "ldap" +address = "ldap://localhost:3893" +base-dn = "dc=example,dc=org" + +[directory."default".bind] +dn = "cn=serviceuser,ou=svcaccts,dc=example,dc=org" +secret = "mysecret" + +[directory."default".cache] +entries = 500 +ttl = {positive = '1h', negative = '10m'} + +[directory."default".options] +catch-all = true +#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } +subaddressing = true +#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } +superuser-group = "superusers" + +[directory."default".pool] +max-connections = 10 +min-connections = 0 +max-lifetime = "30m" +idle-timeout = "10m" +connect-timeout = "30s" + +[directory."default".filter] +name = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(uid=?))" +email = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=?)(mailAlias=?)(mailList=?)))" +verify = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*?*)(mailAlias=*?*)))" +expand = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(mailList=?))" +domains = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*@?)(mailAlias=*@?)))" + +[directory."default".object-classes] +user = "posixAccount" +group = "posixGroup" + +[directory."default".attributes] +name = "uid" +description = ["principalName", "description"] +secret = "userPassword" +groups = ["memberOf", "otherGroups"] +email = "mail" +email-alias = "mailAlias" +quota = "diskQuota" + diff --git a/resources/config/directory/lmtp.toml b/resources/config/directory/lmtp.toml new file mode 100644 index 00000000..824c9169 --- /dev/null +++ b/resources/config/directory/lmtp.toml @@ -0,0 +1,31 @@ +############################################# +# Directory configuration +############################################# + +[directory."lmtp"] +type = "lmtp" +address = "127.0.0.1" +port = 11200 + +[directory."lmtp".limits] +auth-errors = 3 +rcpt = 5 + +[directory."lmtp".pool] +max-connections = 10 +min-connections = 0 +max-lifetime = "30m" +idle-timeout = "10m" +connect-timeout = "30s" + +[directory."lmtp".tls] +implicit = false +allow-invalid-certs = true + +[directory."lmtp".cache] +entries = 500 +ttl = {positive = '1h', negative = '10m'} + +[directory."lmtp".lookup] +domains = ["__DOMAIN__"] + diff --git a/resources/config/directory/memory.toml b/resources/config/directory/memory.toml new file mode 100644 index 00000000..18b9e37e --- /dev/null +++ b/resources/config/directory/memory.toml @@ -0,0 +1,47 @@ +############################################# +# Directory configuration +############################################# + +[directory."default"] +type = "memory" + +[directory."default".options] +catch-all = true +#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } +subaddressing = true +#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } +superuser-group = "superusers" + +[[directory."default".users]] +name = "admin" +description = "Superuser" +secret = "changeme" +email = ["postmaster@__DOMAIN__"] +member-of = ["superusers"] + +[[directory."default".users]] +name = "jane" +description = "Jane Doe" +secret = "abcde" +email = ["jane@__DOMAIN__", "jane.doe@__DOMAIN__"] +email-list = ["info@__DOMAIN__"] +member-of = ["sales", "support"] + +[[directory."default".users]] +name = "bill" +description = "Bill Foobar" +secret = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe" +quota = 50000000 +email = ["bill@__DOMAIN__", "bill.foobar@__DOMAIN__"] +email-list = ["info@__DOMAIN__"] + +[[directory."default".groups]] +name = "sales" +description = "Sales Team" + +[[directory."default".groups]] +name = "support" +description = "Support Team" + +[directory."default".lookup] +domains = ["__DOMAIN__"] diff --git a/resources/config/directory/sql.toml b/resources/config/directory/sql.toml new file mode 100644 index 00000000..54217283 --- /dev/null +++ b/resources/config/directory/sql.toml @@ -0,0 +1,40 @@ +############################################# +# Directory configuration +############################################# + +[directory."default"] +type = "sql" +address = "sqlite://__PATH__/data/accounts.sqlite3?mode=rwc" + +[directory."default".options] +catch-all = true +#catch-all = { map = "(.+)@(.+)$", to = "info@${2}" } +subaddressing = true +#subaddressing = { map = "^([^.]+)\.([^.]+)@(.+)$", to = "${2}@${3}" } +superuser-group = "superusers" + +[directory."default".pool] +max-connections = 10 +min-connections = 0 +#idle-timeout = "10m" + +[directory."default".cache] +entries = 500 +ttl = {positive = '1h', negative = '10m'} + +[directory."default".query] +name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = ? AND active = true" +members = "SELECT member_of FROM group_members WHERE name = ?" +recipients = "SELECT name FROM emails WHERE address = ?" +emails = "SELECT address FROM emails WHERE name = ? AND type != 'list' ORDER BY type DESC, address ASC" +verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type = 'primary' ORDER BY address LIMIT 5" +expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50" +domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1" + +[directory."default".columns] +name = "name" +description = "description" +secret = "secret" +email = "address" +quota = "quota" +type = "type" diff --git a/resources/config/imap/listener.toml b/resources/config/imap/listener.toml new file mode 100644 index 00000000..9e207e5f --- /dev/null +++ b/resources/config/imap/listener.toml @@ -0,0 +1,17 @@ +############################################# +# IMAP server listeners configuration +############################################# + +[server.listener."imap"] +bind = ["[::]:143"] +protocol = "imap" + +[server.listener."imaptls"] +bind = ["[::]:993"] +protocol = "imap" +tls.implicit = true + +[server.listener."sieve"] +bind = ["[::]:4190"] +protocol = "managesieve" +tls.implicit = true diff --git a/resources/config/imap.toml b/resources/config/imap/settings.toml similarity index 57% rename from resources/config/imap.toml rename to resources/config/imap/settings.toml index b7141aaf..8d7864cf 100644 --- a/resources/config/imap.toml +++ b/resources/config/imap/settings.toml @@ -1,21 +1,7 @@ ############################################# -# IMAP server configuration +# IMAP server settings ############################################# -[server.listener."imap"] -bind = ["[::]:143"] -protocol = "imap" - -[server.listener."imaptls"] -bind = ["[::]:993"] -protocol = "imap" -tls.implicit = true - -[server.listener."sieve"] -bind = ["[::]:4190"] -protocol = "managesieve" -tls.implicit = true - [imap.request] max-size = 52428800 diff --git a/resources/config/jmap.toml b/resources/config/jmap.toml deleted file mode 100644 index 54170f37..00000000 --- a/resources/config/jmap.toml +++ /dev/null @@ -1,179 +0,0 @@ -############################################# -# JMAP server configuration -############################################# - -[server.listener."jmap"] -bind = ["[::]:8080"] -url = "https://__HOST__:8080" -protocol = "jmap" - -[store.db] -path = "__PATH__/data/index.sqlite3" - -[store.db.pool] -max-connections = 10 -#workers = 8 - -[store.db.cache] -size = 1000 - -[store.blob] -type = "__BLOB_STORE__" - -[store.blob.local] -path = "__PATH__/data/blobs" - -[store.blob.s3] -bucket = "stalwart" -region = "eu-central-1" -access-key = "minioadmin" -secret-key = "minioadmin" -#endpoint = "" -#security-token = "" -#profile = "" -timeout = "30s" - -[jmap] -directory = "__DIRECTORY__" - -[jmap.http] -#headers = ["Access-Control-Allow-Origin: *", -# "Access-Control-Allow-Methods: POST, GET, HEAD, OPTIONS", -# "Access-Control-Allow-Headers: *"] - -[jmap.encryption] -enable = true -append = false - -[jmap.session.cache] -ttl = "1h" -size = 100 - -[jmap.protocol.get] -max-objects = 500 - -[jmap.protocol.set] -max-objects = 500 - -[jmap.protocol.request] -max-concurrent = 4 -max-size = 10000000 -max-calls = 16 - -[jmap.protocol.query] -max-results = 5000 - -[jmap.protocol.upload] -max-size = 50000000 -max-concurrent = 4 -ttl = "1h" - -[jmap.protocol.upload.quota] -files = 1000 -size = 50000000 - -[jmap.protocol.changes] -max-results = 5000 - -[jmap.rate-limit] -account = "1000/1m" -authentication = "10/1m" -anonymous = "100/1m" -use-forwarded = false - -[jmap.rate-limit.cache] -size = 1024 - -[jmap.mailbox] -max-depth = 10 -max-name-length = 255 - -[jmap.email] -max-attachment-size = 50000000 -max-size = 75000000 - -[jmap.email.parse] -max-items = 10 - -[jmap.principal] -allow-lookups = true - -[jmap.sieve] -disable-capabilities = [] -notification-uris = ["mailto"] -protected-headers = ["Original-Subject", "Original-From", "Received", "Auto-Submitted"] - -[jmap.sieve.limits] -name-length = 512 -max-scripts = 256 -script-size = 102400 -string-length = 4096 -variable-name-length = 32 -variable-size = 4096 -nested-blocks = 15 -nested-tests = 15 -nested-foreverypart = 3 -match-variables = 30 -local-variables = 128 -header-size = 1024 -includes = 3 -nested-includes = 3 -cpu = 5000 -redirects = 1 -received-headers = 10 -outgoing-messages = 3 - -[jmap.sieve.vacation] -default-subject = "Automated reply" -subject-prefix = "Auto: " - -[jmap.sieve.default-expiry] -vacation = "30d" -duplicate = "7d" - -[jmap.event-source] -throttle = "1s" - -[jmap.web-sockets] -throttle = "1s" -timeout = "10m" -heartbeat = "1m" - -[jmap.push] -max-total = 100 -throttle = "1ms" - -[jmap.push.attempts] -interval = "1m" -max = 3 - -[jmap.push.retry] -interval = "1s" - -[jmap.push.timeout] -request = "10s" -verify = "1s" - -[jmap.fts] -default-language = "en" - -[oauth] -key = "__OAUTH_KEY__" - -[oauth.auth] -max-attempts = 3 - -[oauth.expiry] -user-code = "30m" -auth-code = "10m" -token = "1h" -refresh-token = "30d" -refresh-token-renew = "4d" - -[oauth.cache] -size = 128 - -[jmap.purge.schedule] -db = "0 3 *" -blobs = "30 3 *" -sessions = "15 * *" diff --git a/resources/config/jmap/auth.toml b/resources/config/jmap/auth.toml new file mode 100644 index 00000000..5d628edd --- /dev/null +++ b/resources/config/jmap/auth.toml @@ -0,0 +1,10 @@ +############################################# +# JMAP authentication & session configuration +############################################# + +[jmap] +directory = "default" + +[jmap.session.cache] +ttl = "1h" +size = 100 diff --git a/resources/config/jmap/listener.toml b/resources/config/jmap/listener.toml new file mode 100644 index 00000000..86b91e07 --- /dev/null +++ b/resources/config/jmap/listener.toml @@ -0,0 +1,8 @@ +############################################# +# JMAP server listener configuration +############################################# + +[server.listener."jmap"] +bind = ["[::]:8080"] +url = "https://__HOST__:8080" +protocol = "jmap" diff --git a/resources/config/jmap/oauth.toml b/resources/config/jmap/oauth.toml new file mode 100644 index 00000000..62c932b0 --- /dev/null +++ b/resources/config/jmap/oauth.toml @@ -0,0 +1,19 @@ +############################################# +# JMAP OAuth server configuration +############################################# + +[oauth] +key = "__OAUTH_KEY__" + +[oauth.auth] +max-attempts = 3 + +[oauth.expiry] +user-code = "30m" +auth-code = "10m" +token = "1h" +refresh-token = "30d" +refresh-token-renew = "4d" + +[oauth.cache] +size = 128 diff --git a/resources/config/jmap/protocol.toml b/resources/config/jmap/protocol.toml new file mode 100644 index 00000000..0d04585f --- /dev/null +++ b/resources/config/jmap/protocol.toml @@ -0,0 +1,48 @@ +############################################# +# JMAP protocol configuration +############################################# + +[jmap.protocol.get] +max-objects = 500 + +[jmap.protocol.set] +max-objects = 500 + +[jmap.protocol.request] +max-concurrent = 4 +max-size = 10000000 +max-calls = 16 + +[jmap.protocol.query] +max-results = 5000 + +[jmap.protocol.upload] +max-size = 50000000 +max-concurrent = 4 +ttl = "1h" + +[jmap.protocol.upload.quota] +files = 1000 +size = 50000000 + +[jmap.protocol.changes] +max-results = 5000 + +[jmap.mailbox] +max-depth = 10 +max-name-length = 255 + +[jmap.email] +max-attachment-size = 50000000 +max-size = 75000000 + +[jmap.email.parse] +max-items = 10 + +[jmap.principal] +allow-lookups = true + +[jmap.http] +#headers = ["Access-Control-Allow-Origin: *", +# "Access-Control-Allow-Methods: POST, GET, HEAD, OPTIONS", +# "Access-Control-Allow-Headers: *"] diff --git a/resources/config/jmap/push.toml b/resources/config/jmap/push.toml new file mode 100644 index 00000000..21111c1a --- /dev/null +++ b/resources/config/jmap/push.toml @@ -0,0 +1,21 @@ +############################################# +# JMAP Push & EventSource configuration +############################################# + +[jmap.push] +max-total = 100 +throttle = "1ms" + +[jmap.push.attempts] +interval = "1m" +max = 3 + +[jmap.push.retry] +interval = "1s" + +[jmap.push.timeout] +request = "10s" +verify = "1s" + +[jmap.event-source] +throttle = "1s" diff --git a/resources/config/jmap/ratelimit.toml b/resources/config/jmap/ratelimit.toml new file mode 100644 index 00000000..96ee0b6e --- /dev/null +++ b/resources/config/jmap/ratelimit.toml @@ -0,0 +1,13 @@ + +############################################# +# JMAP server rate limiter configuration +############################################# + +[jmap.rate-limit] +account = "1000/1m" +authentication = "10/1m" +anonymous = "100/1m" +use-forwarded = false + +[jmap.rate-limit.cache] +size = 1024 diff --git a/resources/config/jmap/sieve.toml b/resources/config/jmap/sieve.toml new file mode 100644 index 00000000..fda509ec --- /dev/null +++ b/resources/config/jmap/sieve.toml @@ -0,0 +1,36 @@ +############################################# +# JMAP Sieve interpreter configuration +############################################# + +[jmap.sieve] +disable-capabilities = [] +notification-uris = ["mailto"] +protected-headers = ["Original-Subject", "Original-From", "Received", "Auto-Submitted"] + +[jmap.sieve.limits] +name-length = 512 +max-scripts = 256 +script-size = 102400 +string-length = 4096 +variable-name-length = 32 +variable-size = 4096 +nested-blocks = 15 +nested-tests = 15 +nested-foreverypart = 3 +match-variables = 30 +local-variables = 128 +header-size = 1024 +includes = 3 +nested-includes = 3 +cpu = 5000 +redirects = 1 +received-headers = 10 +outgoing-messages = 3 + +[jmap.sieve.vacation] +default-subject = "Automated reply" +subject-prefix = "Auto: " + +[jmap.sieve.default-expiry] +vacation = "30d" +duplicate = "7d" diff --git a/resources/config/jmap/store.toml b/resources/config/jmap/store.toml new file mode 100644 index 00000000..0dd5ab8d --- /dev/null +++ b/resources/config/jmap/store.toml @@ -0,0 +1,41 @@ +############################################# +# JMAP server store configuration +############################################# + +[store.db] +path = "__PATH__/data/index.sqlite3" + +[store.db.pool] +max-connections = 10 +#workers = 8 + +[store.db.cache] +size = 1000 + +[store.blob] +type = "__BLOB_STORE__" + +[store.blob.local] +path = "__PATH__/data/blobs" + +[store.blob.s3] +bucket = "stalwart" +region = "eu-central-1" +access-key = "minioadmin" +secret-key = "minioadmin" +#endpoint = "" +#security-token = "" +#profile = "" +timeout = "30s" + +[jmap.encryption] +enable = true +append = false + +[jmap.fts] +default-language = "en" + +[jmap.purge.schedule] +db = "0 3 *" +blobs = "30 3 *" +sessions = "15 * *" diff --git a/resources/config/jmap/websockets.toml b/resources/config/jmap/websockets.toml new file mode 100644 index 00000000..e9018e1e --- /dev/null +++ b/resources/config/jmap/websockets.toml @@ -0,0 +1,8 @@ +############################################# +# JMAP WebSockets server configuration +############################################# + +[jmap.web-sockets] +throttle = "1s" +timeout = "10m" +heartbeat = "1m" diff --git a/resources/config/macros.toml b/resources/config/macros.toml new file mode 100644 index 00000000..61d99244 --- /dev/null +++ b/resources/config/macros.toml @@ -0,0 +1,8 @@ +############################################# +# Configuration file macros +############################################# + +[macros] +default_domain = "__HOST__" +base_path = "__BASE_PATH__" + diff --git a/resources/config/smtp.toml b/resources/config/smtp.toml deleted file mode 100644 index f5b2bb31..00000000 --- a/resources/config/smtp.toml +++ /dev/null @@ -1,395 +0,0 @@ -############################################# -# SMTP server configuration -############################################# - -[server.listener."smtp"] -bind = ["[::]:25"] -#greeting = "Stalwart SMTP at your service" -protocol = "smtp" - -[server.listener."submission"] -bind = ["[::]:587"] -protocol = "smtp" - -[server.listener."submissions"] -bind = ["[::]:465"] -protocol = "smtp" -tls.implicit = true - -[server.listener."management"] -bind = ["127.0.0.1:8080"] -protocol = "http" - -[session] -timeout = "5m" -transfer-limit = 262144000 # 250 MB -duration = "10m" - -[session.connect] -#script = "connect.sieve" - -[session.ehlo] -require = true -reject-non-fqdn = [ { if = "listener", eq = "smtp", then = true}, - { else = false } ] -#script = "ehlo" - -[session.extensions] -pipelining = true -chunking = true -requiretls = true -no-soliciting = "" -dsn = [ { if = "authenticated-as", ne = "", then = true}, - { else = false } ] -expn = [ { if = "authenticated-as", ne = "", then = true}, - { else = false } ] -vrfy = [ { if = "authenticated-as", ne = "", then = true}, - { else = false } ] -future-release = [ { if = "authenticated-as", ne = "", then = "7d"}, - { else = false } ] -deliver-by = [ { if = "authenticated-as", ne = "", then = "15d"}, - { else = false } ] -mt-priority = [ { if = "authenticated-as", ne = "", then = "mixer"}, - { else = false } ] - -[session.auth] -mechanisms = [ { if = "listener", ne = "smtp", then = ["plain", "login"]}, - { else = [] } ] -directory = [ { if = "listener", ne = "smtp", then = "__SMTP_DIRECTORY__" }, - { else = false } ] -require = [ { if = "listener", ne = "smtp", then = true}, - { else = false } ] -allow-plain-text = false - -[session.auth.errors] -total = 3 -wait = "5s" - -[session.mail] -#script = "mail-from" -#rewrite = [ { all-of = [ { if = "listener", ne = "smtp" }, -# { if = "rcpt", matches = "^([^.]+)@([^.]+)\.(.+)$"}, -# ], then = "${1}@${3}" }, -# { else = false } ] - -[session.rcpt] -#script = "rcpt-to" -relay = [ { if = "authenticated-as", ne = "", then = true }, - { else = false } ] -#rewrite = [ { all-of = [ { if = "rcpt-domain", in-list = "__SMTP_DIRECTORY__/domains" }, -# { if = "rcpt", matches = "^([^.]+)\.([^.]+)@(.+)$"}, -# ], then = "${1}+${2}@${3}" }, -# { else = false } ] -max-recipients = 25 -directory = "__SMTP_DIRECTORY__" - -[session.rcpt.errors] -total = 5 -wait = "5s" - -[session.data] -#script = "data" - -#[session.data.milter."rspamd"] -#enable = [ { if = "listener", eq = "smtp", then = true }, -# { else = false } ] -#hostname = "127.0.0.1" -#port = 11332 -#tls = false -#allow-invalid-certs = false - -#[session.data.milter."rspamd".timeout] -#connect = "30s" -#command = "30s" -#data = "60s" - -#[session.data.milter."rspamd".options] -#tempfail-on-error = true -#max-response-size = 52428800 # 50mb -#version = 6 - -#[session.data.pipe."spam-assassin"] -#command = "spamc" -#arguments = [] -#timeout = "10s" - -[session.data.limits] -messages = 10 -size = 104857600 -received-headers = 50 - -[session.data.add-headers] -received = [ { if = "listener", eq = "smtp", then = true }, - { else = false } ] -received-spf = [ { if = "listener", eq = "smtp", then = true }, - { else = false } ] -auth-results = [ { if = "listener", eq = "smtp", then = true }, - { else = false } ] -message-id = [ { if = "listener", eq = "smtp", then = false }, - { else = true } ] -date = [ { if = "listener", eq = "smtp", then = false }, - { else = true } ] -return-path = false - -[[session.throttle]] -#match = {if = "remote-ip", eq = "10.0.0.1"} -key = ["remote-ip"] -concurrency = 5 -#rate = "5/1h" - -[[session.throttle]] -key = ["sender-domain", "rcpt"] -rate = "25/1h" - -[auth.dnsbl] -verify = [ { if = "listener", eq = "smtp", then = ["ip", "iprev", "ehlo", "return-path", "from"] }, - { else = [] } ] - -[auth.dnsbl.lookup] -ip = ["zen.spamhaus.org", "bl.spamcop.net", "b.barracudacentral.org"] -domain = ["dbl.spamhaus.org"] - -[auth.iprev] -verify = [ { if = "listener", eq = "smtp", then = "relaxed" }, - { else = "disable" } ] - -[auth.dkim] -verify = "relaxed" -sign = [ { if = "listener", ne = "smtp", then = ["rsa"] }, - { else = [] } ] - -[auth.spf.verify] -ehlo = [ { if = "listener", eq = "smtp", then = "relaxed" }, - { else = "disable" } ] -mail-from = [ { if = "listener", eq = "smtp", then = "relaxed" }, - { else = "disable" } ] - -[auth.arc] -verify = "relaxed" -seal = ["rsa"] - -[auth.dmarc] -verify = [ { if = "listener", eq = "smtp", then = "relaxed" }, - { else = "disable" } ] - -[queue] -path = "__PATH__/queue" -hash = 64 - -[queue.schedule] -retry = ["2m", "5m", "10m", "15m", "30m", "1h", "2h"] -notify = ["1d", "3d"] -expire = "5d" - -[queue.outbound] -#hostname = "__HOST__" -next-hop = [ { if = "rcpt-domain", in-list = "__SMTP_DIRECTORY__/domains", then = "__NEXT_HOP__" }, - { else = false } ] -ip-strategy = "ipv4-then-ipv6" - -[queue.outbound.tls] -dane = "optional" -mta-sts = "optional" -starttls = "require" -allow-invalid-certs = false - -#[queue.outbound.source-ip] -#v4 = ["10.0.0.10", "10.0.0.11"] -#v6 = ["a::b", "a::c"] - -[queue.outbound.limits] -mx = 7 -multihomed = 2 - -[queue.outbound.timeouts] -connect = "3m" -greeting = "3m" -tls = "2m" -ehlo = "3m" -mail-from = "3m" -rcpt-to = "3m" -data = "10m" -mta-sts = "2m" - -[[queue.quota]] -#match = {if = "sender-domain", eq = "foobar.org"} -#key = ["rcpt"] -messages = 100000 -size = 10737418240 # 10gb - -[[queue.throttle]] -key = ["rcpt-domain"] -#rate = "100/1h" -concurrency = 5 - -[resolver] -type = "system" -#preserve-intermediates = true -concurrency = 2 -timeout = "5s" -attempts = 2 -try-tcp-on-error = true -public-suffix = ["https://publicsuffix.org/list/public_suffix_list.dat", - "file://__PATH__/etc/lists/public-suffix-list.dat.gz"] - -[resolver.cache] -txt = 2048 -mx = 1024 -ipv4 = 1024 -ipv6 = 1024 -ptr = 1024 -tlsa = 1024 -mta-sts = 1024 - -[report] -path = "__PATH__/reports" -hash = 64 -#submitter = "__HOST__" - -[report.analysis] -addresses = ["dmarc@*", "abuse@*", "postmaster@*"] -forward = true -#store = "__PATH__/incoming" - -[report.dsn] -from-name = "Mail Delivery Subsystem" -from-address = "MAILER-DAEMON@__DOMAIN__" -sign = ["rsa"] - -[report.dkim] -from-name = "Report Subsystem" -from-address = "noreply-dkim@__DOMAIN__" -subject = "DKIM Authentication Failure Report" -sign = ["rsa"] -send = "1/1d" - -[report.spf] -from-name = "Report Subsystem" -from-address = "noreply-spf@__DOMAIN__" -subject = "SPF Authentication Failure Report" -send = "1/1d" -sign = ["rsa"] - -[report.dmarc] -from-name = "Report Subsystem" -from-address = "noreply-dmarc@__DOMAIN__" -subject = "DMARC Authentication Failure Report" -send = "1/1d" -sign = ["rsa"] - -[report.dmarc.aggregate] -from-name = "DMARC Report" -from-address = "noreply-dmarc@__DOMAIN__" -org-name = "__DOMAIN__" -#contact-info = "" -send = "daily" -max-size = 26214400 # 25mb -sign = ["rsa"] - -[report.tls.aggregate] -from-name = "TLS Report" -from-address = "noreply-tls@__DOMAIN__" -org-name = "__DOMAIN__" -#contact-info = "" -send = "daily" -max-size = 26214400 # 25 mb -sign = ["rsa"] - -[signature."rsa"] -#public-key = "file://__PATH__/etc/dkim/__DOMAIN__.cert" -private-key = "file://__PATH__/etc/dkim/__DOMAIN__.key" -domain = "__DOMAIN__" -selector = "stalwart" -headers = ["From", "To", "Date", "Subject", "Message-ID"] -algorithm = "rsa-sha256" -canonicalization = "relaxed/relaxed" -#expire = "10d" -#third-party = "" -#third-party-algo = "" -#auid = "" -set-body-length = false -report = true - -[remote."lmtp"] -address = "127.0.0.1" -port = 11200 -protocol = "lmtp" -concurrency = 10 -timeout = "1m" - -[remote."lmtp".tls] -implicit = false -allow-invalid-certs = true - -#[remote."lmtp".auth] -#username = "" -#secret = "" - -[sieve] -from-name = "Automated Message" -from-addr = "no-reply@__DOMAIN__" -return-path = "" -#hostname = "__HOST__" -sign = ["rsa"] - -[sieve.limits] -redirects = 3 -out-messages = 5 -received-headers = 50 -cpu = 10000 -nested-includes = 5 -duplicate-expiry = "7d" - -[sieve.scripts] -# Note: These scripts are included here for demonstration purposes. -# They should not be used in their current form. -connect = ''' - require ["variables", "extlists", "reject"]; - - if string :list "${env.remote_ip}" "__SMTP_DIRECTORY__/blocked-ips" { - reject "Your IP '${env.remote_ip}' is not welcomed here."; - } -''' -ehlo = ''' - require ["variables", "extlists", "reject"]; - - if string :list "${env.helo_domain}" "__SMTP_DIRECTORY__/blocked-domains" { - reject "551 5.1.1 Your domain '${env.helo_domain}' has been blacklisted."; - } -''' -mail = ''' - require ["variables", "envelope", "reject"]; - - if envelope :localpart :is "from" "known_spammer" { - reject "We do not accept SPAM."; - } -''' -rcpt = ''' - require ["variables", "vnd.stalwart.expressions", "envelope", "reject"]; - - set "triplet" "${env.remote_ip}.${envelope.from}.${envelope.to}"; - - if eval "!query('sql', 'SELECT 1 FROM greylist WHERE addr=? LIMIT 1', [triplet])" { - eval "query('sql', 'INSERT INTO greylist (addr) VALUES (?)', [triplet])"; - reject "422 4.2.2 Greylisted, please try again in a few moments."; - } -''' -data = ''' - require ["envelope", "variables", "replace", "mime", "foreverypart", "editheader", "extracttext"]; - - if envelope :domain :is "to" "foobar.net" { - set "counter" "a"; - foreverypart { - if header :mime :contenttype "content-type" "text/html" { - extracttext :upper "text_content"; - replace "${text_content}"; - } - set :length "part_num" "${counter}"; - addheader :last "X-Part-Number" "${part_num}"; - set "counter" "${counter}a"; - } - } -''' - -[management] -directory = "__DIRECTORY__" diff --git a/resources/config/smtp/auth.toml b/resources/config/smtp/auth.toml new file mode 100644 index 00000000..d124ab42 --- /dev/null +++ b/resources/config/smtp/auth.toml @@ -0,0 +1,28 @@ + +############################################# +# SMTP DMARC, DKIM, SPF, ARC & IpRev +############################################# + +[auth.iprev] +verify = [ { if = "listener", eq = "smtp", then = "relaxed" }, + { else = "disable" } ] + +[auth.dkim] +verify = "relaxed" +sign = [ { if = "listener", ne = "smtp", then = ["rsa"] }, + { else = [] } ] + +[auth.spf.verify] +ehlo = [ { if = "listener", eq = "smtp", then = "relaxed" }, + { else = "disable" } ] +mail-from = [ { if = "listener", eq = "smtp", then = "relaxed" }, + { else = "disable" } ] + +[auth.arc] +verify = "relaxed" +seal = ["rsa"] + +[auth.dmarc] +verify = [ { if = "listener", eq = "smtp", then = "relaxed" }, + { else = "disable" } ] + diff --git a/resources/config/smtp/listener.toml b/resources/config/smtp/listener.toml new file mode 100644 index 00000000..8106ae36 --- /dev/null +++ b/resources/config/smtp/listener.toml @@ -0,0 +1,24 @@ +############################################# +# SMTP server listener configuration +############################################# + +[server.listener."smtp"] +bind = ["[::]:25"] +#greeting = "Stalwart SMTP at your service" +protocol = "smtp" + +[server.listener."submission"] +bind = ["[::]:587"] +protocol = "smtp" + +[server.listener."submissions"] +bind = ["[::]:465"] +protocol = "smtp" +tls.implicit = true + +[server.listener."management"] +bind = ["127.0.0.1:8080"] +protocol = "http" + +[management] +directory = "default" diff --git a/resources/config/smtp/milter.toml b/resources/config/smtp/milter.toml new file mode 100644 index 00000000..0c208e5d --- /dev/null +++ b/resources/config/smtp/milter.toml @@ -0,0 +1,26 @@ +############################################# +# SMTP inbound Milter configuration +############################################# + +#[session.data.milter."rspamd"] +#enable = [ { if = "listener", eq = "smtp", then = true }, +# { else = false } ] +#hostname = "127.0.0.1" +#port = 11332 +#tls = false +#allow-invalid-certs = false + +#[session.data.milter."rspamd".timeout] +#connect = "30s" +#command = "30s" +#data = "60s" + +#[session.data.milter."rspamd".options] +#tempfail-on-error = true +#max-response-size = 52428800 # 50mb +#version = 6 + +#[session.data.pipe."spam-assassin"] +#command = "spamc" +#arguments = [] +#timeout = "10s" diff --git a/resources/config/smtp/queue.toml b/resources/config/smtp/queue.toml new file mode 100644 index 00000000..f1f4141c --- /dev/null +++ b/resources/config/smtp/queue.toml @@ -0,0 +1,53 @@ +############################################# +# SMTP server queue configuration +############################################# + +[queue] +path = "__PATH__/queue" +hash = 64 + +[queue.schedule] +retry = ["2m", "5m", "10m", "15m", "30m", "1h", "2h"] +notify = ["1d", "3d"] +expire = "5d" + +[queue.outbound] +#hostname = "__HOST__" +next-hop = [ { if = "rcpt-domain", in-list = "__SMTP_DIRECTORY__/domains", then = "__NEXT_HOP__" }, + { else = false } ] +ip-strategy = "ipv4-then-ipv6" + +[queue.outbound.tls] +dane = "optional" +mta-sts = "optional" +starttls = "require" +allow-invalid-certs = false + +#[queue.outbound.source-ip] +#v4 = ["10.0.0.10", "10.0.0.11"] +#v6 = ["a::b", "a::c"] + +[queue.outbound.limits] +mx = 7 +multihomed = 2 + +[queue.outbound.timeouts] +connect = "3m" +greeting = "3m" +tls = "2m" +ehlo = "3m" +mail-from = "3m" +rcpt-to = "3m" +data = "10m" +mta-sts = "2m" + +[[queue.quota]] +#match = {if = "sender-domain", eq = "foobar.org"} +#key = ["rcpt"] +messages = 100000 +size = 10737418240 # 10gb + +[[queue.throttle]] +key = ["rcpt-domain"] +#rate = "100/1h" +concurrency = 5 diff --git a/resources/config/smtp/remote.toml b/resources/config/smtp/remote.toml new file mode 100644 index 00000000..29d8d09b --- /dev/null +++ b/resources/config/smtp/remote.toml @@ -0,0 +1,19 @@ +############################################# +# SMTP remote servers configuration +############################################# + +[remote."lmtp"] +address = "127.0.0.1" +port = 11200 +protocol = "lmtp" +concurrency = 10 +timeout = "1m" + +[remote."lmtp".tls] +implicit = false +allow-invalid-certs = true + +#[remote."lmtp".auth] +#username = "" +#secret = "" + diff --git a/resources/config/smtp/report.toml b/resources/config/smtp/report.toml new file mode 100644 index 00000000..3fecc555 --- /dev/null +++ b/resources/config/smtp/report.toml @@ -0,0 +1,57 @@ +############################################# +# SMTP reporting configuration +############################################# + +[report] +path = "__PATH__/reports" +hash = 64 +#submitter = "__HOST__" + +[report.analysis] +addresses = ["dmarc@*", "abuse@*", "postmaster@*"] +forward = true +#store = "__PATH__/incoming" + +[report.dsn] +from-name = "Mail Delivery Subsystem" +from-address = "MAILER-DAEMON@__DOMAIN__" +sign = ["rsa"] + +[report.dkim] +from-name = "Report Subsystem" +from-address = "noreply-dkim@__DOMAIN__" +subject = "DKIM Authentication Failure Report" +sign = ["rsa"] +send = "1/1d" + +[report.spf] +from-name = "Report Subsystem" +from-address = "noreply-spf@__DOMAIN__" +subject = "SPF Authentication Failure Report" +send = "1/1d" +sign = ["rsa"] + +[report.dmarc] +from-name = "Report Subsystem" +from-address = "noreply-dmarc@__DOMAIN__" +subject = "DMARC Authentication Failure Report" +send = "1/1d" +sign = ["rsa"] + +[report.dmarc.aggregate] +from-name = "DMARC Report" +from-address = "noreply-dmarc@__DOMAIN__" +org-name = "__DOMAIN__" +#contact-info = "" +send = "daily" +max-size = 26214400 # 25mb +sign = ["rsa"] + +[report.tls.aggregate] +from-name = "TLS Report" +from-address = "noreply-tls@__DOMAIN__" +org-name = "__DOMAIN__" +#contact-info = "" +send = "daily" +max-size = 26214400 # 25 mb +sign = ["rsa"] diff --git a/resources/config/smtp/resolver.toml b/resources/config/smtp/resolver.toml new file mode 100644 index 00000000..3f48a104 --- /dev/null +++ b/resources/config/smtp/resolver.toml @@ -0,0 +1,23 @@ +############################################# +# SMTP server resolver configuration +############################################# + +[resolver] +type = "system" +#preserve-intermediates = true +concurrency = 2 +timeout = "5s" +attempts = 2 +try-tcp-on-error = true +public-suffix = ["https://publicsuffix.org/list/public_suffix_list.dat", + "file://%%{BASE_PATH}%%/etc/spamfilter/maps/suffix_list.dat.gz"] + +[resolver.cache] +txt = 2048 +mx = 1024 +ipv4 = 1024 +ipv6 = 1024 +ptr = 1024 +tlsa = 1024 +mta-sts = 1024 + diff --git a/resources/config/smtp/session.toml b/resources/config/smtp/session.toml new file mode 100644 index 00000000..a11e7c95 --- /dev/null +++ b/resources/config/smtp/session.toml @@ -0,0 +1,102 @@ +############################################# +# SMTP inbound session configuration +############################################# + +[session] +timeout = "5m" +transfer-limit = 262144000 # 250 MB +duration = "10m" + +[session.connect] +#script = "connect.sieve" + +[session.ehlo] +require = true +reject-non-fqdn = [ { if = "listener", eq = "smtp", then = true}, + { else = false } ] +#script = "ehlo" + +[session.extensions] +pipelining = true +chunking = true +requiretls = true +no-soliciting = "" +dsn = [ { if = "authenticated-as", ne = "", then = true}, + { else = false } ] +expn = [ { if = "authenticated-as", ne = "", then = true}, + { else = false } ] +vrfy = [ { if = "authenticated-as", ne = "", then = true}, + { else = false } ] +future-release = [ { if = "authenticated-as", ne = "", then = "7d"}, + { else = false } ] +deliver-by = [ { if = "authenticated-as", ne = "", then = "15d"}, + { else = false } ] +mt-priority = [ { if = "authenticated-as", ne = "", then = "mixer"}, + { else = false } ] + +[session.auth] +mechanisms = [ { if = "listener", ne = "smtp", then = ["plain", "login"]}, + { else = [] } ] +directory = [ { if = "listener", ne = "smtp", then = "__SMTP_DIRECTORY__" }, + { else = false } ] +require = [ { if = "listener", ne = "smtp", then = true}, + { else = false } ] +allow-plain-text = false + +[session.auth.errors] +total = 3 +wait = "5s" + +[session.mail] +#script = "mail-from" +#rewrite = [ { all-of = [ { if = "listener", ne = "smtp" }, +# { if = "rcpt", matches = "^([^.]+)@([^.]+)\.(.+)$"}, +# ], then = "${1}@${3}" }, +# { else = false } ] + +[session.rcpt] +#script = "greylist" +relay = [ { if = "authenticated-as", ne = "", then = true }, + { else = false } ] +#rewrite = [ { all-of = [ { if = "rcpt-domain", in-list = "__SMTP_DIRECTORY__/domains" }, +# { if = "rcpt", matches = "^([^.]+)\.([^.]+)@(.+)$"}, +# ], then = "${1}+${2}@${3}" }, +# { else = false } ] +max-recipients = 25 +directory = "__SMTP_DIRECTORY__" + +[session.rcpt.errors] +total = 5 +wait = "5s" + +[session.data] +script = [ { if = "authenticated-as", eq = "", then = "spam-filter"}, + { else = "track-replies" } ] + +[session.data.limits] +messages = 10 +size = 104857600 +received-headers = 50 + +[session.data.add-headers] +received = [ { if = "listener", eq = "smtp", then = true }, + { else = false } ] +received-spf = [ { if = "listener", eq = "smtp", then = true }, + { else = false } ] +auth-results = [ { if = "listener", eq = "smtp", then = true }, + { else = false } ] +message-id = [ { if = "listener", eq = "smtp", then = false }, + { else = true } ] +date = [ { if = "listener", eq = "smtp", then = false }, + { else = true } ] +return-path = false + +[[session.throttle]] +#match = {if = "remote-ip", eq = "10.0.0.1"} +key = ["remote-ip"] +concurrency = 5 +#rate = "5/1h" + +[[session.throttle]] +key = ["sender-domain", "rcpt"] +rate = "25/1h" diff --git a/resources/config/smtp/sieve.toml b/resources/config/smtp/sieve.toml new file mode 100644 index 00000000..7c725d3c --- /dev/null +++ b/resources/config/smtp/sieve.toml @@ -0,0 +1,35 @@ +############################################# +# SMTP Sieve interpreter configuration +############################################# + +[sieve] +from-name = "Automated Message" +from-addr = "no-reply@__DOMAIN__" +return-path = "" +#hostname = "__HOST__" +sign = ["rsa"] + +[sieve.limits] +redirects = 3 +out-messages = 5 +received-headers = 50 +cpu = 50000 +nested-includes = 5 +duplicate-expiry = "7d" + +[sieve.scripts] +#connect = '''require ["variables", "extlists", "reject"]; +# if string :list "${env.remote_ip}" "__SMTP_DIRECTORY__/blocked-ips" { +# reject "Your IP '${env.remote_ip}' is not welcomed here."; +# }''' + +#ehlo = '''require ["variables", "extlists", "reject"]; +# if string :list "${env.helo_domain}" "__SMTP_DIRECTORY__/blocked-domains" { +# reject "551 5.1.1 Your domain '${env.helo_domain}' has been blacklisted."; +# }''' + +#mail = '''require ["variables", "envelope", "reject"]; +# if envelope :localpart :is "from" "known_spammer" { +# reject "We do not accept SPAM."; +# }''' + diff --git a/resources/config/smtp/signature.toml b/resources/config/smtp/signature.toml new file mode 100644 index 00000000..ebd6e89a --- /dev/null +++ b/resources/config/smtp/signature.toml @@ -0,0 +1,18 @@ +############################################# +# SMTP DKIM & ARC signatures +############################################# + +[signature."rsa"] +#public-key = "file://__PATH__/etc/dkim/__DOMAIN__.cert" +private-key = "file://__PATH__/etc/dkim/__DOMAIN__.key" +domain = "__DOMAIN__" +selector = "stalwart" +headers = ["From", "To", "Date", "Subject", "Message-ID"] +algorithm = "rsa-sha256" +canonicalization = "relaxed/relaxed" +#expire = "10d" +#third-party = "" +#third-party-algo = "" +#auid = "" +set-body-length = false +report = true diff --git a/resources/config/smtp/spamfilter.toml b/resources/config/smtp/spamfilter.toml new file mode 100644 index 00000000..dd78abfb --- /dev/null +++ b/resources/config/smtp/spamfilter.toml @@ -0,0 +1,111 @@ +############################################# +# SMTP Spam & Phishing filter configuration +############################################# + +[directory."spamdb"] +type = "sql" +address = "sqlite://__PATH__/data/spamfilter.sqlite3" + +[directory."spamdb".pool] +max-connections = 10 +min-connections = 0 +idle-timeout = "5m" + +[directory."spamdb".lookup] +token-insert = "INSERT INTO bayes_tokens (h1, h2, ws, wh) VALUES (?, ?, ?, ?) + ON CONFLICT(h1, h2) + DO UPDATE SET ws = ws + excluded.ws, wh = wh + excluded.wh" +token-lookup = "SELECT ws, wh FROM bayes_tokens WHERE h1 = ? AND h2 = ?" +id-insert = "INSERT INTO seen_ids (id, ttl) VALUES (?, datetime('now', ? || ' seconds'))" +id-lookup = "SELECT 1 FROM seen_ids WHERE id = ? AND ttl > CURRENT_TIMESTAMP" +reputation-insert = "INSERT INTO reputation (token, score, count, ttl) VALUES (?, ?, 1, datetime('now', '30 days')) + ON CONFLICT(token) + DO UPDATE SET score = (count + 1) * (excluded.score + 0.98 * score) / (0.98 * count + 1), count = count + 1, ttl = excluded.ttl" +reputation-lookup = "SELECT score, count FROM reputation WHERE token = ?" + +[directory."spamdb".schedule] +query = ["DELETE FROM seen_ids WHERE ttl < CURRENT_TIMESTAMP", + "DELETE FROM reputation WHERE ttl < CURRENT_TIMESTAMP"] +frequency = "0 3 *" + +[directory."spam".lookup."free-domains"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/domains_free.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/domains_free.list"] + +[directory."spam".lookup."disposable-domains"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/domains_disposable.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/domains_disposable.list"] + +[directory."spam".lookup."redirectors"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/url_redirectors.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/url_redirectors.list"] + +[directory."spam".lookup."domains-allow"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/allow_domains.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/allow_domains.list"] + +[directory."spam".lookup."dmarc-allow"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/allow_dmarc.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/allow_dmarc.list"] + +[directory."spam".lookup."spf-dkim-allow"] +type = "glob" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/allow_spf_dkim.list", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/allow_spf_dkim.list"] + +[directory."spam".lookup."mime-types"] +type = "map" +comment = '#' +values = ["https://get.stalw.art/resources/etc/spamfilter/maps/mime_types.map", + "file+fallback://%%{BASE_PATH}%%/etc/spamfilter/maps/mime_types.map"] + +[directory."spam".lookup."trap-address"] +type = "glob" +comment = '#' +values = "file://%%{BASE_PATH}%%/etc/spamfilter/maps/spam_trap.list" + +[directory."spam".lookup."scores"] +type = "map" +values = "file://%%{BASE_PATH}%%/etc/spamfilter/maps/scores.map" + +[sieve.scripts] +spam-filter = ["file://%%{BASE_PATH}%%/etc/spamfilter/scripts/config", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/prelude", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/from", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/recipient", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/subject", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/replyto", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/date", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/messageid", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/received", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/headers", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/bounce", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/html", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/mime", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/dmarc", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/ip", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/helo", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/replies_in", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/spamtrap", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/bayes_classify", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/url", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/rbl", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/pyzor", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/scores", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/reputation", + "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/epilogue"] + +track-replies = "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/replies_out.sieve" + +greylist = "file://%%{BASE_PATH}%%/etc/spamfilter/scripts/greylist.sieve" diff --git a/resources/config/spamfilter/maps/allow_dmarc.list b/resources/config/spamfilter/maps/allow_dmarc.list new file mode 100644 index 00000000..b67cea3e --- /dev/null +++ b/resources/config/spamfilter/maps/allow_dmarc.list @@ -0,0 +1,786 @@ +18f.gov +1password.com +2gis.com +4chan.org +4pda.ru +9-11commission.gov +911.gov +aberdeenshire.gov.uk +abilityone.gov +absolutbank.ru +access-board.gov +acquisition.gov +acus.gov +ada.gov +adf.gov +adidas.co.kr +adidas.com.au +adidas.com.br +adidas.com.hk +adidas.fi +adidas.fr +admongo.gov +adobe.dk +adobe.es +adobeawards.com +adp.com +advice.hmrc.gov.uk +aerocivil.gov.co +aerocivil.gov.co +afreximbank.com +agingstats.gov +agro.ru +ahcpr.gov +aids.gov +airbnb.co.uk +airbnb.com +airbnb.com.tr +airbnb.cz +airbnb.de +airbnb.fi +airbnb.fr +airbnb.pl +airbnb.ru +airbnb.se +airnow.gov +airtel.in +alfabank.com +alfabank.ru +alfastrah.ru +alibaba.com +aliexpress.com +alipay.com +alkupone.ru +alzheimers.gov +amazon.co.uk +amazon.com +amazon.com.br +amberalert.gov +americanexpress.com +ameslab.gov +angus.gov.uk +anidub.com +annapolis.gov +anstaskforce.gov +apple.com.au +apple.com.cn +apps.gov +archives.gov +arctic.gov +arionbanki.is +asg.com +asic.gov.au +askona.ru +asos.com +assist.ru +atf.gov +avast.com +avg.com +avito.ru +avtoradio.ru +axisbank.com +badoo.com +baltbank.ru +bank.lv +banki.ru +bankofamerica.com +barclaycard.co.uk +barclays.co.uk +barclays.com +battle.net +beeline.kz +beeline.ru +benefits.gov +betfaq.ru +biglion.ru +binary.com +binbank.ru +bioethics.gov +biometrics.gov +biopreferred.gov +birminghampost.net +bishopsmove.com +bitbank.cc +bjs.gov +blizzard.com +blog.gov.uk +bls.gov +bluestarindia.com +boemre.gov +bolsover.gov.uk +bolton.gov.uk +booking.com +bookmate.com +books.ru +bournemouth.gov.uk +box.com +bpa.gov +brandshop.ru +bridgend.gov.uk +brighton-hove.gov.uk +britishembassy.gov.uk +broadbandmap.gov +bromley.gov.uk +bts.gov +business.gov +caerphilly.gov.uk +caixa.gov.br +cambridge-news.co.uk +campaign.gov.uk +cancer.gov +cannockchasedc.gov.uk +capitalone.co.uk +cardiff.gov.uk +carecredit.com +cbp.gov +cdfifund.gov +centralbedfordshire.gov.uk +ceredigion.gov.uk +cesg.gov.uk +cfda.gov +cfo.gov +challenge.gov +change.org +chase.com +chcoc.gov +childstats.gov +cio.gov +circle.com +citibank.ae +citibank.co.in +citibank.co.uk +citibank.com.my +citibank.hu +citibank.pl +cloud.gov +cloudflare.com +cms.gov +co-operativebank.co.uk +colgate.com.br +collegedrinkingprevention.gov +companies-house.gov.uk +comuneap.gov.it +conab.gov.br +concerts.com +consultant.ru +contact-sys.com +copeland.gov.uk +cosla.gov.uk +courtservice.gov.uk +coventry.gov.uk +cre.gov.uk +csosa.gov +cuidadodesalud.gov +culturarecreacionydeporte.gov.co +customs.gov.my +customs.gov.ua +cybercrime.gov +dailypost.co.uk +danskebank.dk +danskebank.fi +danskebank.ie +danskebank.no +dartford.gov.uk +dartmoor.gov.uk +dataprotection.gov.uk +daventrydc.gov.uk +dellin.ru +denbighshire.gov.uk +deutsche-bank.de +deutschebank.be +deutschebank.co.in +dh.gov.uk +dhl.com +dhs.gov +diablo3.com +digital.gov +digitalgov.gov +digitalliteracy.gov +disability.gov +disability.gov.uk +dnfsb.gov +docker.com +docusign.net +doe.gov +doioig.gov +dol.gov +doleta.gov +domofond.ru +drought.gov +drugabuse.gov +dsns.gov.ua +dtv.gov +dudley.gov.uk +dyslexiaida.org +e-boks.dk +e-verify.gov +eastdunbarton.gov.uk +eaststaffsbc.gov.uk +eastsuffolk.gov.uk +ebay.be +ebay.ca +ebay.ch +ebay.co.uk +ebay.com +ebay.com.au +ebay.com.cn +ebay.de +ebay.es +ebay.eu +ebay.fr +ebay.in +ebay.it +ebay.se +economy.gov.tr +econsumer.gov +ed.gov +eftps.gov +ehsni.gov.uk +eia.gov +ejob.gov.tw +elance.com +eldorado.ru +email-ee.co.uk +email.tektorg.ru +emarsys.com +ems.gov +energystar.gov +erewash.gov.uk +esetnod32.ru +essex-fire.gov.uk +eubank.kz +evernote.com +everychildmatters.gov.uk +evus.gov +exist.ru +expediamail.com +facebook.com +facebookmail.com +fbi.gov +fcc.gov +fco.gov.uk +fdic.gov +feb.gov +federalreserve.gov +fedex.com +fedramp.gov +fedshirevets.gov +feedthefuture.gov +fema.gov +ferc.gov +fhfa.gov +fhfaoig.gov +fife.gov.uk +financialresearch.gov +financialstability.gov +firstbankpb.bank +firstbankpb.com +firstnet.gov +firstresponder.gov +fishwatch.gov +fitness.gov +flagma.ua +flamp.ru +fletc.gov +fmc.gov +fmcs.gov +foia.gov +food.gov.uk +force.com +fordlibrarymuseum.gov +foreignassistance.gov +foreigntrade.gov.tr +franklinwi.gov +ftc.gov +ftccomplaintassistant.gov +gamereactor.dk +gap.com +garant.ru +geekbrains.ru +geektimes.ru +getsmartaboutdrugs.gov +gibraltar.gov.uk +gitlab.com +globalentry.gov +globalhealth.gov +globe.gov +gloucestershire.gov.uk +goes-r.gov +gosuslugi.ru +gov.uk +groupon.es +groupon.hk +groupon.it +gsa.gov +gsaadvantage.gov +gsaauctions.gov +gsaig.gov +gtbank.com +guideline.gov +guidelines.gov +gwynedd.gov.uk +habr.com +hambleton.gov.uk +harp.gov +hawaiicounty.gov +hdfcbank.com +hdrezka.ag +healthcare.gov +healthypeople.gov +hertfordshire.gov.uk +hh.kz +hh.ru +highland.gov.uk +highwaycode.gov.uk +hillingdon.gov.uk +hiv.gov +hmrc.gov.uk +homeoffice.gov.uk +homesales.gov +hotels.com +hounslow.gov.uk +howto.gov +hru.gov +huduser.gov +hurricanes.gov +iba.gov.au +ice.gov +idmanagement.gov +ikea.ch +ikea.co.uk +ikea.com +ikea.de +ikea.fr +ikea.gr +ikea.nl +ikea.pl +imgur.com +incometaxindiaefiling.gov.in +ing.com +inl.gov +inlandrevenue.gov.uk +insider.co.uk +insolvency.gov.uk +instagram.com +insurekidsnow.gov +invasivespeciesinfo.gov +investor.gov +irda.gov.in +itunes.com +jccbi.gov +jd.ru +jet.com +jimmycarterlibrary.gov +job.com +johnsonsbaby.co.uk +joybuy.com +jpmorgan.com +jpmorgansecurities.com +judiciary.gov.uk +justice.gov +justice.gov.az +justice.gov.uk +jyskebank.dk +kassy.ru +kent.gov.uk +keys.openpgp.org +kids.gov +kingston.gov.uk +kivra.com +klarna.com +klarna.se +kpk.gov.pl +lacoast.gov +landsbanki.is +lanl.gov +lbhf.gov.uk +lcd.gov.uk +learningcurve.gov.uk +leeds.gov.uk +leroymerlin.es +lichfielddc.gov.uk +lincoln.gov.uk +lincolnshire.gov.uk +linkedin.com +livejournal.com +llnl.gov +lloydsbank.com +locatorplus.gov +lostfilm.tv +louisvilleco.gov +love.ru +lufthansa-group.com +lufthansa.com +mackeeper.com +mailgun.net +mak.com +mandtbank.com +mcc.gov +mcga.gov.uk +mchenrycountyil.gov +mecknc.gov +mediamarkt.se +medicaid.gov +medicare.gov +medium.com +megafon.ru +megaplan.ru +mercadolibre.com.ar +mercadolivre.com.br +merseyfire.gov.uk +merthyr.gov.uk +meshok.ru +messenger.com +microsoft.net +middlesbrough.gov.uk +midlothian.gov.uk +mil.ru +mincit.gov.co +minhacienda.gov.co +minsvyaz.ru +mintic.gov.co +mirrorfootball.co.uk +mkb.ru +mlg.ru +mlg.tv +mns.gov.ua +mod.gov.az +molisa.gov.vn +mos.ru +mosoblbank.ru +mosreg.ru +motinfo.gov.uk +movavi.com +msha.gov +mspb.gov +msport.gov.pl +murfreesborotn.gov +mvideo.ru +mxtoolbox.com +mymoney.gov +myplate.gov +myra.gov +myshared.ru +n-kesteven.gov.uk +n-somerset.gov.uk +nads.gov.ua +nalog.ru +namus.gov +nasa.gov +nationalarchives.gov.uk +nationalservice.gov +nationsreportcard.gov +nbr.gov.bd +nbtbank.com +ncifcrf.gov +ncpw.gov +nctb.gov.bd +ne-derbyshire.gov.uk +nea.gov +nelincs.gov.uk +neobux.com +neolane.net +netflix.com +newegg.com +newmoney.gov +nga.gov +ngu.gov.ua +nhtsa.gov +nic.ru +nidw.gov.bd +nij.gov +nio.gov.uk +niscc.gov.uk +nist.gov +nixonlibrary.gov +nkh.gov.hu +noaa.gov +nordea.dk +nordea.com +nordea.fi +nordea.no +nordea.se +north-ayrshire.gov.uk +north-norfolk.gov.uk +northlincs.gov.uk +norwich.gov.uk +notifications.service.gov.uk +nottinghamcity.gov.uk +nrc-gateway.gov +nrc.gov +nrel.gov +nsf.gov +nsopr.gov +nsopw.gov +nwtrb.gov +oculus.com +ofcm.gov +office.com +officemag.ru +ok.ru +omb.gov +ombudsman.gov.tr +onedrive.com +onguardonline.gov +opengl.org +openinternet.gov +ordsvy.gov.uk +ornl.gov +oshrc.gov +osti.gov +oxfordshire.gov.uk +ozon.ru +paauditor.gov +paccar.com +paddle8.com +pandemicflu.gov +passport.gov.uk +payeer.com +paymentaccuracy.gov +paypal-community.com +paypal.be +paypal.cn +paypal.co.il +paypal.co.uk +paypal.com +paypal.com.au +paypal.com.br +paypal.com.mx +paypal.de +paypal.dk +paypal.es +paypal.fr +paypal.nl +paypal.se +pbgc.gov +pc.gov.au +pch.com +penanghill.gov.my +pepfar.gov +performance.gov +pinterest.co.kr +pinterest.com +pinterest.de +pinterest.jp +pinterest.se +pkc.gov.uk +planeta.ru +platron.ru +plymouth.gov.uk +pm.gov.uk +pmf.gov +pmi.gov +pncbank.com +pokerstars.com +pokerstars.fr +pokerstars.it +pokerstars.net +priorbank.by +privatbank.ua +prospertx.gov +prostocash.com +provident.bank +psbank.ru +psc.gov +punjab.gov.in +puzzle-english.com +qiwi.com +qiwi.ru +rabota.ru +rbkc.gov.uk +ready.gov +reaganlibrary.gov +redbridge.gov.uk +reddit.com +reebok.es +reebok.nl +reginfo.gov +regulations.gov +reisebank.de +renfrewshire.gov.uk +rentonwa.gov +reportband.gov +rgs.ru +richmond.gov.uk +rivers.gov +rkn.gov.ru +ros.gov.uk +roseltorg.ru +rostelecom.ru +roundrocktexas.gov +royalmail.com +rozetka.com.ua +rt.com +rt.ru +rushcliffe.gov.uk +rutubeinfo.ru +sacn.gov.uk +safercar.gov +samhsa.gov +sanmarcostx.gov +sberbank.ru +sberbank-ast.ru +sbir.gov +sbis.ru +scality.com +scdhhs.gov +science360.gov +sciencebase.gov +scijinks.gov +sec.gov +secretservice.gov +section508.gov +semnan.ac.ir +senate.gov +sendgrid.net +seniorcorps.gov +serpro.gov.br +service.gov.uk +sftool.gov +shetland.gov.uk +shropshire-cc.gov.uk +shutterstock.com +sigtarp.gov +sk.ru +skat.dk +skatteverket.se +skbbank.ru +skittles.com +skydio.com +skype.com +slideshare.com +smart.gov +smida.gov.ua +smokefree.gov +snickers.com +solardecathlon.gov +sourceforge.net +south-ayrshire.gov.uk +sovest.ru +spbrealty.ru +sportmaster.ru +squarespace.com +sravni.ru +srs.gov +staffordbc.gov.uk +stat.gov.az +stedmundsbury.gov.uk +sthelens.gov.uk +stihl.ru +stopalcoholabuse.gov +stopfraud.gov +studentloans.gov +subscribe.ru +suffolkcc.gov.uk +suncorpbank.com.au +sundaymirror.co.uk +sunlight.net +superjob.ru +surestart.gov.uk +sutton.gov.uk +swansea.gov.uk +swift.com +symantec.com +synologynotification.com +taobao.com +tatar.ru +tauntondeane.gov.uk +tda.gov.uk +tdk.gov.tr +tdscpc.gov.in +telework.gov +tenders.gov.au +textmagic.com +tfhrc.gov +thebell.io +thecoolspot.gov +thinkroadsafety.gov.uk +tiaabank.com +ticketland.ru +tinder.com +tinkoff.ru +tomsk.gov.ru +torbay.gov.uk +tradingstandards.gov.uk +treas.gov +trial-sport.ru +tsa.gov +tst.gov.br +tuba.gov.tr +turystyka.gov.pl +tutu.ru +twitch.tv +twitter.com +twix.com +uber.com +ucarecdn.com +ucrdatatool.gov +udall.gov +ukvisas.gov.uk +ulmart.ru +unicor.gov +uniras.gov.uk +ups.com +uralairlines.ru +us-cert.gov +usa.gov +usadf.gov +usaid.gov +usap.gov +uscg.gov +usconsulate.gov +usmission.gov +usphs.gov +uspis.gov +usps.com +usps.gov +ustreas.gov +utair.ru +utkonos.ru +vaccines.gov +valeofglamorgan.gov.uk +verizonwireless.com +vigoda.ru +visa.co.uk +visa.com +visa.com.ar +visa.com.br +visa.com.cn +visa.com.tw +visa.pl +vistacampus.gov +vk.com +vkrugudruzei.ru +vkusnyblog.ru +vmc.gov.in +voa.gov.uk +volunteer.gov +vote.gov +walsall.gov.uk +wandsworth.gov.uk +wartimecontracting.gov +warwickdc.gov.uk +wealden.gov.uk +wellingtonfl.gov +west-lindsey.gov.uk +westernunion.com +westernunion.ru +westlothian.gov.uk +whatsapp.com +whistleblowers.gov +wirral.gov.uk +wlga.gov.uk +womenshealth.gov +wrexham.gov.uk +wrigley.com +wrp.gov +yandex-team.ru +york.gov.uk +youla.ru +youth.gov +youthrules.gov +youtube.com +zcts.ru +zendesk.com +zionsbank.com +zomato.com diff --git a/resources/config/spamfilter/maps/allow_domains.list b/resources/config/spamfilter/maps/allow_domains.list new file mode 100644 index 00000000..219b737c --- /dev/null +++ b/resources/config/spamfilter/maps/allow_domains.list @@ -0,0 +1,829 @@ +# Domains listed here are not checked against RBL +126.com +163.com +1gost.info +1stnationalbank.com +2o7.net +365online.com +4at1.com +53.com +5iantlavalamp.com +abl.com.pk +about.com +accessbankplc.com +adelphia.net +adib.ae +adobe.com +agora-inc.com +agoramedia.com +aibgb.co.uk +aib.ie +airdriesavingsbank.com +akamai.net +akamaitech.net +aldermore.co.uk +alexa.com +alliancebank.com.my +alliancefg.com +alliantcreditunion.com +alliantcreditunion.org +allianz.de +allybank.com +alterna.ca +amazon.com +americanexpress.ch +americanexpress.com +anadolubank.nl +ancestry.com +anz.com +anz.co.nz +aol.com +apache.org +apple.com +arbuthnotlatham.co.uk +arcamax.com +asb.co.nz +ask.com +astrology.com +atdmt.com +att.net +authorize.net +autorambler.ru +axisbank.co.in +axisbank.com +b2bbank.com +baaderbank.de +baidu.com +baloise.ch +baml.com +banamex.com +bancanetbsc.do +bancanetsantacruz.com.do +bancapulia.it +bancarios.com +bancastato.ch +bancatransilvania.ro +bancobase.com +bancobic.ao +bancobic.pt +bancobpi.pt +banco.bradesco +bancobrasil.com.br +bancochile.cl +bancochile.com +bancoestado.cl +bancofalabella.cl +bancofalabella.com.co +bancofalabella.pe +bancomer.com +bancopopolare.it +bancopostaclick.it +bancoposta.it +bancosantander.es +bancovotorantimcartoes.com.br +bank24.ru +bankalhabib.com +bankaustria.at +bank.barclays.co.uk +bankbgzbnpparibas.pl +bankcardservices.co.uk +bankcomm.com +bankcoop.ch +bankiabancapersonal.es +bankia.com +bankia.es +bankinter.com +bankinter.es +bankmutual.com +bankofamerica.com +bankofcanada.ca +bankofchina.com +bankofcyprus.com +bankofindia.co.nz +bankofireland.com +bank-of-ireland.co.uk +bankofirelanduk.com +bankofoklahoma.com +bankofscotland.co.uk +banksinarmas.com +bankvonroll.ch +bankwest.com.au +banque-casino.fr +banquepopulaire.fr +banquescotia.com +barclaycard.co.uk +barclaycard.de +barclaycard.es +barclays.com +barclays.co.uk +barclayspartnerfinance.com +barclays.sc +barodanzltd.co.nz +basler.ch +bbandt.com +bbc.co.uk +bcentral.com +bci.cl +bcp.com.pe +bcv.ch +bcvs.ch +bekb.ch +bellevue.ch +bellsouth.net +bendigobank.com.au +berliner-bank.de +berliner-sparkasse.de +bfanet.ao +bfi0.com +bgfi.com +bgfionline.com +bgzbnpparibas.pl +billmelater.com +bing.com +bkb.ch +bk.rw +bks.at +blkb.ch +bmocm.com +bmo.com +bmogam.com +bmoharris.com +bmoharrisprivatebankingonline.com +bmoinvestorline.com +bmonesbittburns.com +bnl.it +bnpparibas.com +bnpparibas.fr +boc.cnnz +bonuscard.ch +bpe-gruposantander.com +bpi.pt +bpostbank.be +bradescardonline.com.br +bradesco.com.br +bradescoseguranca.com.br +bridgetrack.com +bridgewaterbank.ca +bsibank.com +btrl.ro +bt-trade.ro +businessonline-boi.com +bzbank.ch +ca-cib.com +ca-egypt.com +cafbank.org +cafe24.com +cafonline.org +caisse-epargne.com +caisse-epargne.fr +caixabank.com +caixa.gov.br +cajasur.es +camsonline.com +canadiandirect.com +capitalone360.com +capitalone.com +capitaloneonline.co.uk +capitecbank.co.za +cariparma.it +carrefour-banque.fr +cartabcc.it +cartabccpos.it +cartasi.it +ca-suisse.com +catalunyacaixa.com +cbg.gm +cbonline.co.uk +cembra.ch +cenbank.org +centralbank.ae +charitybank.org +charter.net +chase.com +chebanca.it +chinatrust.com.tw +cial.ch +cibc.com +cic.ch +cimbclicks.com.my +citibank.ae +citibank.co.in +citibank.com +citibank.co.uk +citibankonline.com +citibusiness.com +citicards.com +citi.com +citi.co.nz +citi.eu +citigroup.com +citizensbank.ca +citizensbank.com +civibank.com +civibank.it +cjb.net +classmates.com +clickbank.net +closebrothers.com +closebrothers.co.uk +clubsc.ch +cnet.com +cnn.com +co.kg +colpatria.com +colpatria.com.co +comcast.net +com.com +commbank.com.au +commerzbank.com +commerzbank.de +com.ne.kr +coopbank.dk +co-operativebank.co.uk +cornerbanca.ch +cornercard.ch +cornercard.com +corner.ch +corporate-ir.net +cosycard.ch +coutts.com +cox.net +craigslist.org +credit-agricole.com +credit-agricole.fr +creditagricole.rs +credit-suisse.com +cs.com +css.ch +ctbcbank.com +ctfs.com +custhelp.com +cwbank.com +cwbankgroup.com +cwt.ca +cybg.com +danskebankas.lt +danskebank.com +danskebank.co.uk +danskebank.de +danskebank.dk +danskebank.ee +danskebank.fi +danskebank.ie +danskebank.no +datatrans.biz +datatrans.ch +daum.net +db.com +dbs.com +dd.se +debian.org +dell.com +demirbank.kg +denizbank.com +desjardins.ca +desjardins.com +deutschebank.be +deutschebank.co.nz +deutsche-bank.de +diamondbank.com +dibpak.com +directnic.com +directtrack.com +discovercard.com +discover.com +discovery.co.za +dnbnord.lt +domain.com +doubleclick.com +dresdner-bank.de +dsbbank.sr +dsbl.org +duncanlawrie.com +earthlink.net +easybank.at +easylnk.com +ebay.com +ebay.co.uk +ebay.de +ebayimg.com +ebaystatic.com +ecobank.com +edgesuite.net +ediets.com +edwardjones.com +egroups.com +e-gulfbank.com +emode.com +esunbank.com.tw +example.com +example.net +example.org +excite.com +facebook.com +fedex.com +fednetbank.com +fidelity.com +fidor.de +finance.com +finansbank.com.tr +finasta.lt +fineco.it +firstbankcard.com +firstmerit.com +firstnational.com +firstnationalmerchantsolutions.com +firsttrustbank.co.uk +flickr.com +fnbc.ca +fnb.co.za +fnb-online.com +freebsd.org +free.fr +friuladria.it +f-secure.com +garantibank.eu +garantibank.nl +garanti.com.tr +gazprombank.ch +gazprombank.ru +generali.es +genevoise.ch +gentoo.org +geocities.com +gkb.ch +gmail.com +gmx.net +go.com +godaddy.com +googleadservices.com +google.co.in +google.com +google.it +google.ru +granitbank.hu +grisoft.com +gtbank.com +halifax.co.uk +hallmark.com +handelsbanken.se +harrodsbank.co.uk +hbl.com +hblibank.com +hblibank.com.pk +hdfcbank.com +heartland.co.nz +hellenicbank.com +hinet.net +hkbea.com +hlb.com.kh +hlb.com.my +hoaresbank.co.uk +home.barclays +hongleongconnect.com.kh +hongleongconnect.com.vn +hongleongconnect.my +hotbar.com +hotmail.com +hotpop.com +hp.com +hsbc.com +hsbc.com.ar +hsbc.com.hk +hsbc.co.nz +hsbc.co.uk +hypovereinsbank.co.uk +hypovereinsbank.de +ibm.com +icbcnz.com +icicibank.co.in +icicibank.com +icicibankprivatebanking.com +icorner.ch +icscards.de +icscards.nl +incredimail.com +ing.be +ing.com +ing-diba.de +ingdirect.ca +ing.lu +ing.nl +ingvysyabank.com +interac.ca +investorplace.com +iobnet.co.in +isbank.com.tr +isbank.de +isbank.ge +isbank.iq +isbankkosova.com +itau.com.br +ivillage.com +joingevalia.com +jpmchase.com +jpmorgan.com +jsafrasarasin.com +julianhodgebank.com +juliusbaer.com +juno.com +jyskebank.dk +kantonalbank.ch +kernel.org +key.com +kiwibank.co.nz +kotak.com +kredytbank.pl +kreissparkasse-schwalm-eder.de +ksklb.de +kutxabank.es +laboralkutxa.com +lacaixa.cat +lacaixa.es +laurentianbank.ca +lbb.de +lcl.com +lcl.fr +li.ru +list.ru +liveinternet.ru +livejournal.com +lloydsbank.com +lloydsbankcommercial.com +lloydsbankinggroup.com +lloydstsb.ch +lloydstsb.co.uk +lombardodier.com +loydsbank.com +lycos.com +m7z.net +mac.com +macromedia.com +maerki-baumann.ch +mail.com +mail.ru +mailscanner.info +mandtbank.com +manulifebank.ca +manulifebankselect.ca +manulife.com +manulifeone.ca +marketwatch.com +mashreqbank.com +mastercard.com +maybank2u.com +maybank2u.com.my +mcafee.com +mchsi.com +mdmbank.com +mechanicsbank.com +medbank.lt +messagelabs.com +metrobankdirect.com +metrobankonline.co.uk +microsoft.com +migbank.com +migrosbank.ch +military.com +mindspring.com +mit.edu +mizuhobank.co.jp +mmwarburg.lu +monster.com +montepio.pt +morganstanley.com +mozilla.com +mps.it +ms.com +msn.com +mufg.jp +myonlineresourcecenter.com +myonlineservices.ch +myspace.com +nate.com +nationalesuisse.ch +nationwide-communications.co.uk +nationwide.co.uk +nationwide-service.co.uk +natwest.com +navyfederal.org +nbc.ca +netflix.com +netscape.com +netscape.net +netzero.net +newyorkfed.org +nibl.com.np +nod32.com +nordea.fi +nordea.lt +nordfynsbank.dk +norisbank.de +norman.com +notenstein.ch +nuvisionfederal.com +nytimes.com +oceanbank.com +onlinesbi.com +openoffice.org +openxmlformats.org +optonline.net +orchardbank.com +osdn.com +ostsaechsische-sparkasse-dresden.de +overstock.com +pacbell.net +pandasoftware.com +passport.com +paylife.at +paypal.be +paypal-brasil.com.br +paypal.ca +paypal.ch +paypal.co.il +paypal.com +paypal.com.au +paypal.com.br +paypal-communication.com +paypal-community.com +paypal.com.mx +paypal.com.pt +paypal.co.uk +paypal-customerfeedback.com +paypal.de +paypal-deutschland.de +paypal.dk +paypal.es +paypal-exchanges.com +paypal.fr +paypal.it +paypal-marketing.co.uk +paypal-marketing.pl +paypal.net +paypal.nl +paypal.no +paypal-notify.com +paypal-now.com +paypalobjects.com +paypal-opwaarderen.nl +paypal-pages.com +paypal.pt +paypal.ru +paypal.se +paypal-search.com +paypal-shopping.co.uk +paypal-techsupport.com +pbebank.com +pcfinancial.ca +peoplepc.com +permanenttsb.ie +plaxo.com +pnc.com +popolarevicenza.it +postbank.de +postepay.it +postfinancearena.ch +postfinance.ch +postfinance.info +price.ru +prodigy.net +publicislamicbank.com.my +rabobank.com +rabobank.co.nz +rabobank.nl +radaruol.com.br +rahnbodmer.ch +raiffeisenbank.rs +raiffeisen.ch +raiffeisen.hu +raiffeisen.li +raiffeisen.ru +rambler-co.ru +rambler.ru +raphaelsbank.com +rbc.com +rbcroyalbank.com +rbs.co.uk +rbssecure.co.uk +rbsworldpay.com +rcb.at +real.com +recordbank.be +redhat.com +rediff.com +regiobank.nl +regions.com +regionsnet.com +renasantbank.com +rhbgroup.com +rogersbank.com +rogers.com +rothschildbank.com +rothschild.com +royalbank.com +rr.com +sagepay.com +sagepay.co.uk +sainsburysbank.co.uk +samba.com +santander.cl +santander.com +santander.com.br +santander.com.mx +santandercorretora.com.br +santander.co.uk +santanderesfera.com.br +santandersantiago.cl +sarasin.ch +sbcglobal.net +sberbank.ch +sbs.net.nz +sc.com +schoellerbank.at +scotiabank.ca +scotiabank.com +scotiamocatta.com +scotiaonline.com +s.de +sec.gov +securetrustbank.com +service-sparkasse.de +serviciobancomer.com +sf.net +shawbrook.co.uk +shaw.ca +shkb.ch +shockwave.com +six-group.com +six-payment-services.com +skrill.com +sls-direkt.de +smithbarney.com +snb.ch +snsbank.nl +societegenerale.fr +sourceforge.net +spamcop.net +sparda-a.de +sparda-bank-hamburg.de +sparda-b.de +sparda-bw.de +sparda-h.de +sparda-hessen.de +sparda-m.de +sparda-ms.de +sparda-n.de +sparda-ostbayern.de +sparda-sw.de +sparda-verband.de +sparda-west.de +sparkasse.at +sparkasse-bank-malta.com +sparkasse-bielefeld.de +sparkasseblog.de +sparkasse-bochum.de +sparkasse.ch +sparkasse.de +sparkasse-gera-greiz.de +sparkasse-hamm.de +sparkasse-heidelberg.de +sparkasse-ingolstadt.de +sparkasse-mittelthueringen.de +speedera.net +sportsline.com +standardbank.com +standardbank.co.za +standardchartered.com.gh +standardchartered.com.my +subscribe.ru +sun.com +suncorpbank.com.au +suntrust.com +swedbank.com +swedbank.ee +swedbank.lt +swedbank.lu +swedbank.se +swisscanto.ch +swisscaution.ch +swissquote.ch +sydbank.dk +sympatico.ca +tails.nl +tangerine.ca +tcb-bank.com.tw +tdbank.com +tdcommercialbanking.com +telus.net +terra.com.br +tescobank.com +ticketmaster.com +tinyurl.com +tiscali.co.uk +tns-counter.ru +tom.com +tone.co.nz +t-online.de +top4top.ru +tsbbank.co.nz +tsb.co.nz +tsb.co.uk +tux.org +twitter.com +ubibanca.com +ubs.com +ulsterbankanytimebanking.co.uk +ulsterbank.co.uk +unibancoconnect.pt +unibanco.pt +unicreditbank.lt +unicredit.eu +unicreditgroup.eu +unicredit.it +unionbankcameroon.com +unionbank.com +unity.co.uk +uob.com.sg +uobgroup.com +uol.com.br +ups.com +usbank.com +valianttrust.com +vaudoise.ch +venetobanca.it +venetobanka.al +verizon.net +versabank.com +videobank.it +virginmoney.com +visa.com.ar +visa.com.br +visaeurope.ch +visaeurope.com +viseca.ch +volksbank.de +volkswagenbank.de +vpbank.com +vr.de +vwbank.de +w3.org +wachovia.com +walmart.com +wamu.com +wanadoo.fr +washingtonpost.com +weatherbug.com +weatherbys.co.uk +web.de +webshots.com +webtv.net +wegelin.ch +wellsfargo.com +wellsfargoemail.com +westernunion.ca +westernunion.com +westernunion.fr +westernunion.se +westpac.com.au +westpac.co.nz +wir.ch +wordpress.com +worldbank.org +worldpay.com +wsj.com +wvb.de +xmlsoap.org +yacht.nl +yahoo.ca +yahoo.co.jp +yahoo.co.kr +yahoo.com +yahoo.com.br +yahoo.co.uk +yahoogroups.com +yandex.net +yandex.ru +ybonline.co.uk +yimg.com +yopi.de +yorkshirebank.co.uk +yourbankcard.com +yoursite.com +youtube.com +zagbank.ca +zdnet.com +zenithbank.com +zkb.ch +zugerkb.ch +vistaprint.dk +vistaprint.com +anpdm.com +dovecot.org +exacttarget.com +github.com +isc.org +lists.isc.org +lists.roundcube.net +svn.apache.org +taggedmail.com +tumblr.com diff --git a/resources/config/spamfilter/maps/allow_phishing.list b/resources/config/spamfilter/maps/allow_phishing.list new file mode 100644 index 00000000..4679bcdc --- /dev/null +++ b/resources/config/spamfilter/maps/allow_phishing.list @@ -0,0 +1,5 @@ +# Used to exclude domains and tlds from phishing checks as they have known legal use + +protect-*.mimecast.com +*.safelinks.protection.outlook.com +urldefense.proofpoint.com diff --git a/resources/config/spamfilter/maps/allow_spf_dkim.list b/resources/config/spamfilter/maps/allow_spf_dkim.list new file mode 100644 index 00000000..eae13b34 --- /dev/null +++ b/resources/config/spamfilter/maps/allow_spf_dkim.list @@ -0,0 +1,238 @@ +1cfresh.com +4chan.org +6pm.com +about.com +addthis.com +adf.ly +adobe.com +adp.com +adschemist.com +airbnb.com +airtel.in +alibaba.com +aliexpress.com +alipay.com +allrecipes.com +amazon.ca +amazon.cn +amazon.co.jp +amazon.com +amazon.co.uk +amazon.de +amazon.es +amazon.fr +amazon.in +amazon.it +amazon.ru +americanexpress.com +ancestry.com +android.com +apple.com +asana.com +att.com +autohome.com.cn +avg.com +aweber.com +badoo.com +bankofamerica.com +basecamp.com +battle.net +bet365.com +biglobe.ne.jp +bitly.com +bleacherreport.com +blogger.com +bloomberg.com +booking.com +box.com +bt.com +capitalone.com +cdiscount.com +change.org +chase.com +cisco.com +citi.com +costco.com +craigslist.org +custhelp.com +dell.com +delta.com +diply.com +discovercard.com +disqus.com +dropbox.com +drweb.com +e-boks.dk +ebay.ca +ebay.com +ebay.com.au +ebay.co.uk +ebay.de +ebay.fr +ebay.in +ebay.it +ebay.ru +etsy.com +evernote.com +expedia.com +facebook.com +fedex.com +fidelity.com +fishki.net +flickr.com +flirchi.com +force.com +freepik.com +gap.com +gawker.com +github.com +gizmodo.com +godaddy.com +googleadservices.com +googleusercontent.com +groupon.com +hdfcbank.com +hgtv.com +hh.ru +hm.com +houzz.com +hubspot.com +icicibank.com +icloud.com +ign.com +imgur.com +immobilienscout24.de +indeed.com +indiatimes.com +infusionsoft.com +instagram.com +intel.com +irctc.co.in +kayak.com +kickstarter.com +kijiji.ca +kotaku.com +letsencrypt.org +libero.it +lifehacker.com +likes.com +linkedin.com +linux.com +list-manage.com +mackeeper.com +mailchimp.com +mashable.com +match.com +mercadolibre.com.ar +mercadolivre.com.br +messenger.com +microsoft.com +microsoftonline.com +minmyndighetspost.se +moikrug.ru +mts.ru +neobux.com +netflix.com +newegg.com +nhk.or.jp +nifty.com +nikkeibp.co.jp +nyaa.se +nytimes.com +odnoklassniki.ru +ofd.yandex.ru +ok.ru +olx.ua +overstock.com +ozon.ru +ozon.travel +pandora.com +paypal.ca +paypal.cn +paypal.com +paypal.com +paypal.co.uk +paypal.de +paypal.es +paypal.fr +paypal.it +paypal.ru +paytm.com +pch.com +pinterest.com +porn.com +priceline.com +quora.com +rakuten.co.jp +reddit.com +researchgate.net +salesforce.com +sciencedirect.com +shopify.com +skanetrafiken.se +skat.dk +skatteverket.se +slack.com +slideshare.net +so-net.ne.jp +southwest.com +spotify.com +springer.com +squarespace.com +stalker.com +steampowered.com +stumbleupon.com +surveymonkey.com +swagbucks.com +taboola.com +taleo.net +taobao.com +target.com +taringa.net +taxi.yandex.ru +tele2.ru +thekitchn.com +tochka.com +tokopedia.com +trello.com +tribunnews.com +trulia.com +tumblr.com +twitter.com +ultimate-guitar.com +ups.com +usaa.com +usbank.com +usps.com +verizon.com +verizonwireless.com +vimeo.com +vine.co +vk.com +vmware.com +vtb24.ru +wahoofitness.com +walmart.com +wav.tv +wellsfargo.com +whatsapp.com +wikia.com +wikimedia.org +wikipedia.org +wildberries.ru +wix.com +wordpress.com +wordpress.org +wp.com +xuite.net +xvideos.com +yelp.com +youtube.com +yts.to +zappos.com +zendesk.com +zippyshare.com +zomato.com +zoom.us +zulily.com +zwift.com diff --git a/resources/config/spamfilter/maps/domains_disposable.list b/resources/config/spamfilter/maps/domains_disposable.list new file mode 100644 index 00000000..13d053e1 --- /dev/null +++ b/resources/config/spamfilter/maps/domains_disposable.list @@ -0,0 +1,427 @@ +0815.ru +0clickemail.com +0wnd.net +0wnd.org +1054733.mail-temp.com +10m.email +10minutemail.com +10minutesmail.com +1secmail.com +1secmail.net +1secmail.org +20minutemail.com +2emailock.com +2prong.com +33mail.com +3d-magical-magnet.ru +4warding.com +90bit.ru +9ox.net +a-bc.net +afrobacon.com +alaki.ga +alivance.com +amilegit.com +amiri.net +anonymbox.com +antichef.com +antichef.net +antispam.de +audio.now.im +awex.icu +barenshop.ru +barryogorman.com +baxomale.ht.cx +beefmilk.com +binkmail.com +bio-muesli.net +bobmail.info +bofthew.com +brefmail.com +bsnow.net +bspamfree.org +bugmenot.com +casualdx.com +centermail.com +cetpass.com +chammy.info +choicemail1.com +choocho-telegram.ru +cool.fr.nf +courriel.fr.nf +courrieltemporaire.com +cuvox.de +dandikmail.com +dayrep.com +dcemail.com +deadspam.com +desoz.com +devnullmail.com +dfg6.kozow.com +dfgh.net +digitalsanctuary.com +dingbone.com +discardmail.com +discardmail.de +dispomail.win +dispomail.xyz +disposableaddress.com +disposeamail.com +dispostable.com +divismail.ru +dlesha.ru +dmaster39.ru +dodgit.com +domremonta-nv.ru +donemail.ru +dontreg.com +dontsendmespam.de +dumpandjunk.com +e-mail.com +e-mail.org +e4ward.com +edu.aiot.ze.cx +ekholotdeeper.ru +email-24x7.com +email60.com +emailate.com +emailay.com +emailias.com +emailmiser.com +emailsensei.com +emailtemporanea.net +emailtemporario.com.br +emailtex.com +emailwarden.com +emailx.at.hm +emailxfer.com +emz.net +exbts.com +explodemail.com +extremail.ru +eyeemail.com +fakeinbox.com +fakeinformation.com +fantasymail.de +filzmail.com +fls4.gleeze.com +fotosta.ru +frapmail.com +fudgerub.com +funny-mom.ru +furycraft.ru +garliclife.com +get2mail.fr +getonemail.com +gishpuppy.com +gnomi.ru +goplaygame.ru +greensloth.com +grr.la +gsrv.co.uk +guerillamail.com +guerrillamail.biz +guerrillamail.com +guerrillamail.de +guerrillamail.info +guerrillamail.net +guerrillamail.org +guerrillamailblock.com +h2ocoffe.ru +haltospam.com +hatespam.org +hidemail.de +hmamail.com +hochsitze.com +hulapla.de +hydraulics360.ru +i.xcode.ro +imails.info +inboxclean.com +inboxclean.org +irish2me.com +isdaq.com +iwi.net +jetable.com +jetable.fr.nf +jetable.net +jetable.org +justyland.ru +kasmail.com +kaspop.com +kemampuan.me +kikoxltd.com +killmail.com +killmail.net +kismail.ru +kkm35.ru +klassmaster.com +klzlk.com +koszmail.pl +kurzepost.de +kusrc.com +lackmail.ru +laoho.com +ldaho.biz +leeching.net +letthemeatspam.com +lhsdv.com +lifebyfood.com +lifeguru.online +light-marketing.ru +lol.ovpn.to +lookugly.com +lortemail.dk +lr78.com +madecassol78.ru +mail-temporaire.fr +mail.mezimages.net +mail333.com +mailbidon.com +mailblocks.com +mailbucket.org +mailcatch.com +maildrop.cc +maildx.com +mailed.ro +mailfreeonline.com +mailfs.com +mailin8r.com +mailinater.com +mailinator.com +mailinator.net +mailinator2.com +mailincubator.com +mailme.ir +mailme.lv +mailmetal.com +mailmetrash.com +mailmoat.com +mailnesia.com +mailnull.com +mailscrap.com +mailshell.com +mailsiphon.com +mailsoul.com +mailtrash.net +mailzilla.com +makemetheking.com +mbx.cc +mega.zik.dj +meinspamschutz.de +meltmail.com +messagebeamer.de +mineblue.ru +mintemail.com +misha-rosestoy.ru +moboinfo.xyz +moncourrier.fr.nf +monemail.fr.nf +monmail.fr.nf +mor19.uu.gl +mt2009.com +mvrht.com +mycleaninbox.net +mymail-in.net +mypartyclip.de +myphantomemail.com +mytempemail.com +mytrashmail.com +neomailbox.com +nepwk.com +nervmich.net +nervtmich.net +netmails.com +netmails.net +neverbox.com +newfilm24.ru +niepodam.pl +no-spam.ws +nomail.xl.cx +nomorespamemails.com +nospam.ze.tc +nospam4.us +nospammail.net +notmailinator.com +notsharingmy.info +nowmymail.com +nurfuerspam.de +objectmail.com +obobbo.com +officialrolex.ru +oneoffemail.com +onewaymail.com +onlinenet.info +oopi.org +ordinaryamerican.net +otherinbox.com +ovpn.to +owlpic.com +p33.org +pancakemail.com +partner1bizmoney.ru +pchelovodstvo-tut.ru +piratesdelivery.ru +pmlep.de +politikerclub.de +pookmail.com +powerbank-russia.ru +privacy.net +proxymail.eu +prtnx.com +putthisinyourspamdatabase.com +quickinbox.com +razinrocks.me +rcpt.at +reallymymail.com +recode.me +reconmail.com +recursor.net +reloadpoint.ru +rtrtr.com +s-sakamas.ru +s0ny.net +safe-mail.net +safersignup.de +safetymail.info +safetypost.de +samogonda.ru +sendspamhere.com +senseless-entertainment.com +sgbteamreborn.imouto.pro +shar-kov.ru +sharklasers.com +shiftmail.com +shitmail.me +shortmail.net +sibmail.com +slaskpost.se +smellfear.com +sneakemail.com +sofimail.com +sogetthis.com +soodonims.com +spam4.me +spambob.net +spambog.com +spambog.de +spambog.ru +spambooger.com +spambox.us +spambox.win +spambox.xyz +spamcannon.com +spamcannon.net +spamcon.org +spamcorptastic.com +spamcowboy.com +spamcowboy.net +spamcowboy.org +spamday.com +spamex.com +spamfree.eu +spamfree24.com +spamfree24.de +spamfree24.org +spamgourmet.com +spamgourmet.net +spamgourmet.org +spamhereplease.com +spamhole.com +spamify.com +spaml.de +spammotel.com +spamobox.com +spamslicer.com +spamthis.co.uk +speed.1s.fr +squizzy.net +sskstroy.ru +streetwisemail.com +super-auswahl.de +supermailer.jp +suremail.info +tecninja.xyz +teewars.org +teleosaurs.xyz +teleworm.com +temp-mail.org +tempe-mail.com +tempemail.com +tempemail.net +tempinbox.co.uk +tempinbox.com +tempmail.it +tempmail.top +tempmail.win +tempomail.fr +thankyou2010.com +thisisnotmyrealemail.com +thrott.com +throwawayemailaddress.com +tilien.com +titaspaharpur1.gq +tmailinator.com +tradermail.info +trash-mail.at +trash-mail.com +trash-mail.de +trash2009.com +trashdevil.com +trashemail.de +trashinbox.net +trashmail.at +trashmail.com +trashmail.de +trashmail.me +trashmail.net +trashmail.org +trashmail.ws +trashmailer.com +trashymail.com +trbvm.com +trbvn.com +trillianpro.com +tvchd.com +twinmail.de +tyldd.com +uggsrock.com +upliftnow.com +urhen.com +ussv.club +vapecentral.ru +venompen.com +veryrealemail.com +vkusup.ru +voemail.com +vssms.com +weammo.xyz +webcool.club +wegwerfadresse.de +wegwerfemail.com +wegwerfemail.de +wegwerfmail.de +wegwerfmail.net +wegwerfmail.org +wh4f.org +whyspam.me +willhackforfood.biz +willselfdestruct.com +wronghead.com +wwwnew.eu +xemaps.com +xitroo.com +xmaily.com +xoxy.net +xww.ro +yep.it +yevme.com +yopmail.com +yopmail.fr +yopmail.net +yuurok.com +zagorodnyi-domik.ru +zdorovpagh.ru +zehnminutenmail.de +zippymail.info +zoemail.net diff --git a/resources/config/spamfilter/maps/domains_free.list b/resources/config/spamfilter/maps/domains_free.list new file mode 100644 index 00000000..93240024 --- /dev/null +++ b/resources/config/spamfilter/maps/domains_free.list @@ -0,0 +1,4007 @@ +0-mail.com +027168.com +0815.su +0sg.net +10mail.org +10minutemail.co.za +123.com +123india.com +123mail.cl +123mail.org +126.com +139.com +150mail.com +150ml.com +15meg4free.com +163.com +16mail.com +188.com +189.cn +1ce.us +1chuan.com +1funplace.com +1internetdrive.com +1mail.net +1me.net +1mum.com +1musicrow.com +1pad.de +1zhuan.com +2-mail.com +20email.eu +20mail.in +20mail.it +212.com +21cn.com +24horas.com +2980.com +2bmail.co.uk +2die4.com +2trom.com +3126.com +321media.com +37.com +3ammagazine.com +3dmail.com +3g.ua +3mail.ga +444.net +4email.net +4mg.com +4warding.net +4x4man.com +50mail.com +5iron.com +60minutemail.com +6ip.us +74.ru +7mail.ml +88.am +8mail.ml +97rock.com +99experts.com +9online.fr +a1.net +a45.in +aaamail.zzn.com +aapt.net.au +aaronkwok.net +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com +abcflash.net +abdulnour.com +aberystwyth.com +about.com +abv.bg +abwesend.de +abyssmail.com +acceso.or.cr +access4less.net +accountant.com +acdcfan.com +acmemail.net +acninc.net +activist.com +adam.com.au +add3000.pp.ua +addcom.de +address.com +adelphia.net +adexec.com +adfarrow.com +adoption.com +ados.fr +adrenalinefreak.com +advalvas.be +advantimo.com +aeiou.pt +africamail.com +africamel.net +agoodmail.com +ahaa.dk +ahk.jp +aichi.com +aim.com +aircraftmail.com +airforce.net +airforceemail.com +airpost.net +ajacied.com +ak47.hu +aknet.kg +albawaba.com +algeria.com +alhilal.net +alibaba.com +alice.it +aliyun.com +allergist.com +allmail.net +allracing.com +allsaintsfan.com +alpenjodel.de +alphafrau.de +alskens.dk +alternativagratis.com +alumni.com +alumnidirector.com +alvilag.hu +amail.com +amele.com +america.hm +ameritech.net +amnetsal.com +amorki.pl +amrer.net +amuro.net +amuromail.com +ananzi.co.za +ancestry.com +andylau.net +angelfire.com +angelic.com +animail.net +animalhouse.com +anjungcafe.com +annsmail.com +ano-mail.net +anonymous.to +anote.com +another.com +anotherdomaincyka.tk +anotherwin95.com +anti-social.com +antisocial.com +antispam24.de +anymoment.com +anytimenow.com +aol.co.uk +aol.com +aol.fr +aon.at +apexmail.com +apmail.com +apollo.lv +aport.ru +aport2000.ru +appraiser.net +arabia.com +arabtop.net +archaeologist.com +arcor.de +arcticmail.com +argentina.com +army.net +armyspy.com +arnet.com.ar +artlover.com +artlover.com.au +asdasd.nl +asean-mail.com +asheville.com +asia-links.com +asia-mail.com +asia.com +asiafind.com +asianavenue.com +asiancityweb.com +asiansonly.net +asianwired.net +asiapoint.net +ass.pp.ua +assala.com +assamesemail.com +astrolover.com +astrosfan.net +asurfer.com +atheist.com +athenachu.net +atina.cl +atozasia.com +atrus.ru +att.net +attglobal.net +attymail.com +au.ru +auctioneer.net +ausi.com +aussiemail.com.au +austin.rr.com +australia.edu +australiamail.com +autoescuelanerja.com +autograf.pl +autorambler.ru +aver.com +avh.hu +awsom.net +axoskate.com +azazazatashkent.tk +azmeil.tk +bachelorboy.com +bachelorgal.com +backpackers.com +backstreet-boys.com +bagherpour.com +baldmama.de +baldpapa.de +ballyfinance.com +bangkok.com +bangkok2000.com +bannertown.net +baptistmail.com +baptized.com +barcelona.com +bareed.ws +bartender.net +baseballmail.com +basketballmail.com +batuta.net +bboy.zzn.com +bcvibes.com +beddly.com +beeebank.com +beenhad.com +beep.ru +beer.com +beethoven.com +belice.com +bell.net +bellair.net +bellsouth.net +berlin.com +berlin.de +bestmail.us +betriebsdirektor.de +bettergolf.net +bharatmail.com +bigassweb.com +bigblue.net.au +bigfoot.com +bigfoot.de +bigger.com +biggerbadder.com +bigmailbox.com +bigmir.net +bigpond.com +bigpond.com.au +bigpond.net.au +bigstring.com +bikemechanics.com +bikeracer.com +bikerider.com +billsfan.com +billsfan.net +bin-wieder-da.de +birdlover.com +bisons.com +bitmail.com +bitpage.net +bizhosting.com +bk.ru +blackplanet.com +blader.com +bladesmail.net +blazemail.com +bleib-bei-mir.de +blockfilter.com +blogmyway.org +bluebottle.com +bluemail.ch +bluemail.dk +blushmail.com +boardermail.com +bodhi.lawlita.com +bol.com.br +bolando.com +bolt.com +boltonfans.com +bombdiggity.com +bootybay.de +boun.cr +bounce.net +bouncr.com +box.az +box.ua +boxemail.com +boxformail.in +boxfrog.com +boximail.com +bradfordfans.com +brasilia.net +brazilmail.com +breathe.com +brennendesreich.de +bresnan.net +brew-master.com +brew-meister.com +briefemail.com +bright.net +britneyclub.com +broadcast.net +brokenvalve.com +brusseler.com +bsdmail.com +btcmail.pw +btinternet.com +buerotiger.de +bullsfan.com +bumpymail.com +bund.us +burnthespam.info +burstmail.info +businessman.net +buyersusa.com +bvimailbox.com +byom.de +c2.hu +c3.hu +c4.com +cabacabana.com +cableone.net +caere.it +calidifontain.be +californiamail.com +callnetuk.com +callsign.net +caltanet.it +camidge.com +canada-11.com +canadianmail.com +canoemail.com +caramail.com +caramail.fr +care2.com +careerbuildermail.com +carioca.net +cartestraina.ro +casablancaresort.com +casema.nl +cash4u.com +casino.com +catchamail.com +catholic.org +catlover.com +cd2.com +cegetel.net +celineclub.com +celtic.com +center-mail.de +centermail.at +centermail.de +centermail.info +centoper.it +centralpets.com +centrum.cz +centrum.sk +centurytel.net +certifiedmail.com +cfl.rr.com +cgac.es +chacuo.net +chaiyomail.com +chammy.info +chandrasekar.net +charmedmail.com +charter.com +charter.net +chat.ru +chattown.com +cheatmail.de +chechnya.conf.work +check.com +check1check.com +cheerful.com +chef.net +chek.com +chello.nl +chemist.com +cheyenneweb.com +chez.com +china.com +chinamail.com +chirk.com +chocaholic.com.au +chong-mail.net +churchusa.com +cia-agent.com +cia.hu +cicciociccio.com +cincinow.net +citiz.net +citlink.net +city-of-birmingham.com +city-of-cambridge.com +city-of-edinburgh.com +city-of-lincoln.com +city-of-liverpool.com +city-of-manchester.com +city-of-oxford.com +city-of-swansea.com +city-of-westminster.com +city-of-westminster.net +city-of-york.net +cityoflondon.org +ckaazaza.tk +claramail.com +classicalfan.com +classicmail.co.za +clerk.com +cliffhanger.com +clixser.com +close2you.net +clrmail.com +club4x4.net +clubalfa.com +clubbers.net +clubducati.com +clubhonda.net +club-internet.fr +clubinternet.fr +clubmember.org +clubnetnoir.com +clubvdo.net +cluemail.com +cmail.net +cmpmail.com +cnnsimail.com +cntv.cn +codec.ro +coder.hu +coid.biz +coldmail.com +collectiblesuperstore.com +collector.org +collegeclub.com +collegemail.com +colleges.com +columbus.rr.com +columbusrr.com +columnist.com +comcast.net +comic.com +communityconnect.com +comprendemail.com +compuserve.com +computer4u.com +computermail.net +conexcol.com +conk.com +connect4free.net +consultant.com +consumerriot.com +contractor.net +coole-files.de +coolgoose.ca +coolgoose.com +coolkiwi.com +coolmail.com +coolmail.net +coolsite.net +cooperation.net +cooperationtogo.net +copacabana.com +cornells.com +corporatedirtbag.com +cotas.net +counsellor.com +cox.com +cox.net +coxinet.net +cracker.hu +crapmail.org +crazedanddazed.com +crazymailing.com +cristianemail.com +critterpost.com +croeso.com +crosshairs.com +crosswinds.net +crwmail.com +cs.com +csinibaba.hu +cuemail.com +curio-city.com +curryworld.de +cute-girl.com +cutey.com +cyber-africa.net +cyber-innovation.club +cyber-matrix.com +cyber-wizard.com +cyber4all.com +cyberbabies.com +cybercafemaui.com +cyberdude.com +cybergal.com +cybergrrl.com +cybermail.net +cybernet.it +cyberservices.com +cyberspace-asia.com +cybertrains.org +cyclefanz.com +cynetcity.com +dabsol.net +dadacasa.com +dailypioneer.com +dallasmail.com +dangerous-minds.com +dasdasdascyka.tk +dawnsonmail.com +dawsonmail.com +dbzmail.com +deadlymob.org +deagot.com +deal-maker.com +dearriba.com +death-star.com +deliveryman.com +deneg.net +depechemode.com +deseretmail.com +desilota.com +deskpilot.com +destin.com +detik.com +deutschland-net.com +devotedcouples.com +dezigner.ru +dfwatson.com +di-ve.com +die-besten-bilder.de +die-genossen.de +die-optimisten.net +diemailbox.de +digibel.be +digital-filestore.de +diplomats.com +directbox.com +dirtracer.com +discard.email +discard.ga +discard.gq +disciples.com +discofan.com +discovery.com +discoverymail.com +disign-revelation.com +dispomail.eu +disposable.com +dispose.it +dm.w3internet.co.uk +dnsmadeeasy.com +docmail.cz +doctor.com +dodo.com.au +dodsi.com +dog.com +dogit.com +doglover.com +dogsnob.net +doityourself.com +domforfb1.tk +domforfb2.tk +domforfb3.tk +domforfb4.tk +domforfb5.tk +domforfb6.tk +domforfb8.tk +domozmail.com +doneasy.com +dontgotmail.com +dontmesswithtexas.com +doramail.com +dostmail.com +dotcom.fr +dotmsg.com +dott.it +download-privat.de +dplanet.ch +dr.com +dropmail.me +dropzone.com +drotposta.hu +dublin.com +dublin.ie +dumpmail.com +dumpmail.de +dumpyemail.com +dunlopdriver.com +dunloprider.com +duno.com +duskmail.com +dutchmail.com +dwp.net +dygo.com +dyndns.org +e-apollo.lv +e-mail.com.tr +e-mail.dk +e-mail.ru +e-mail.ua +e-mailanywhere.com +e-tapaal.com +earthalliance.com +earthcam.net +earthdome.com +earthling.net +earthlink.net +earthonline.net +eastcoast.co.za +eastmail.com +easy.to +easypost.com +easytrashmail.com +ecardmail.com +echina.com +ecompare.com +edmail.com +edtnmail.com +educacao.te.pt +eelmail.com +ehmail.com +einrot.com +eintagsmail.de +eircom.net +elitemail.org +elvis.com +elvisfan.com +email-fake.gq +email-london.co.uk +email.biz +email.com +email.cz +email.ee +email.it +email.nu +email.org +email.ro +email.ru +email.su +email.ua +email2me.net +emailacc.com +emailaccount.com +emailasso.net +emailchoice.com +emailcorner.net +emailem.com +emailengine.net +emailengine.org +emailgo.de +emailgroups.net +emailinfive.com +emailit.com +emailplanet.com +emailplus.org +emailto.de +emailuser.net +emailx.net +embarqmail.com +emeil.in +emeil.ir +emil.com +eml.cc +eml.pp.ua +enel.net +engineer.com +england.com +england.edu +englandmail.com +epage.ru +ephemail.net +epix.net +eposta.hu +eramail.co.za +eresmas.com +eriga.lv +estranet.it +ethos.st +eudoramail.com +europamel.net +europe.com +europemail.com +euroseek.com +eurosport.com +every1.net +everyday.com.kh +everymail.net +everyone.net +everytg.ml +examnotes.net +excite.co.jp +excite.com +excite.it +execs.com +exemail.com.au +expressasia.com +extenda.net +eyepaste.com +ezcybersearch.com +ezrs.com +f-m.fm +f1fans.net +facebook.com +fahr-zur-hoelle.org +fake-email.pp.ua +fake-mail.cf +falseaddress.com +fan.com +fansonlymail.com +fantasticmail.com +farang.net +faroweb.com +fast-email.com +fast-mail.fr +fast-mail.org +fastchevy.com +fastem.com +fastemail.us +fastemailer.com +fastermail.com +fastest.cc +fastimap.com +fastmail.ca +fastmail.cn +fastmail.co.uk +fastmail.com +fastmail.com.au +fastmail.es +fastmail.fm +fastmail.im +fastmail.in +fastmail.jp +fastmail.mx +fastmail.net +fastmail.nl +fastmail.se +fastmail.to +fastmail.tw +fastmail.us +fastmailbox.net +fastmazda.com +fastmessaging.com +fastservice.com +fastsubaru.com +fatflap.com +fathersrightsne.org +fax.ru +fbi.hu +fea.st +federalcontractors.com +feinripptraeger.de +felicitymail.com +femenino.com +fetchmail.co.uk +fettabernett.de +feyenoorder.com +ffanet.com +fiberia.com +ficken.de +fightallspam.com +filipinolinks.com +financemail.net +financier.com +findmail.com +fire-brigade.com +fireman.net +fishburne.org +fishfuse.com +fixmail.tk +fizmail.com +flashbox.5july.org +flashmail.com +flashmail.net +fleckens.hu +flipcode.com +fmail.co.uk +fmailbox.com +fmgirl.com +fmguy.com +fnbmail.co.za +fnmail.com +folkfan.com +foodmail.com +footard.com +football.ua +footballmail.com +for-president.com +force9.co.uk +forgetmail.com +forpresident.com +fortuncity.com +fortunecity.com +forum.dk +foxmail.com +fr33mail.info +francemel.fr +free-online.net +free-org.com +free.com.pe +free.fr +freeaccess.nl +freeaccount.com +freeandsingle.com +freedom.usa.com +freedomlover.com +freegates.be +freelance-france.eu +freeler.nl +freemail.c3.hu +freemail.com.pk +freemail.de +freemail.et +freemail.gr +freemail.hu +freemail.it +freemail.lt +freemail.org.mk +freemails.ga +freemeil.gq +freenet.de +freenet.kg +freeola.com +freeola.net +freestart.hu +freesurf.fr +freesurf.nl +freeuk.com +freeuk.net +freeukisp.co.uk +freeweb.org +freewebemail.com +freeyellow.com +freezone.co.uk +fresnomail.com +freudenkinder.de +freundin.ru +friendlymail.co.uk +friendsfan.com +from-africa.com +from-australia.com +from-europe.com +from-holland.com +from-japan.net +from-mexico.com +from-outerspace.com +from-russia.com +fromalaska.com +fromarizona.com +fromarkansas.com +fromcalifornia.com +fromconnecticut.com +fromgeorgia.com +fromhawaii.net +fromidaho.com +fromindiana.com +fromiowa.com +fromkansas.com +fromlouisiana.com +frommaryland.com +frommassachusetts.com +frommiami.com +frommichigan.com +fromminnesota.com +frommississippi.com +frommissouri.com +fromnevada.com +fromnewhampshire.com +fromnewjersey.com +fromnewmexico.com +fromnorthcarolina.com +fromnorthdakota.com +fromohio.com +fromoklahoma.com +frompennsylvania.com +fromrhodeisland.com +fromru.com +fromsouthcarolina.com +fromtennessee.com +fromtexas.com +fromutah.com +fromvermont.com +fromvirginia.com +fromwashington.com +fromwashingtondc.com +fromwestvirginia.com +fromwisconsin.com +fromwyoming.com +front.ru +frontier.com +frontiernet.net +fsmail.net +ftml.net +fullmail.com +fuorissimo.com +furnitureprovider.com +fuse.net +fut.es +galaxyhit.com +gamebox.net +gamegeek.com +gamespotmail.com +garbage.com +gardener.com +gaybrighton.co.uk +gaza.net +gazeta.pl +gazibooks.com +gci.net +geek.com +geeklife.com +gelitik.in +gencmail.com +gentlemansclub.de +geocities.com +geography.net +geologist.com +geopia.com +germanymail.com +get.pp.ua +get1mail.com +getairmail.com +getairmail.gq +getonemail.net +ghanamail.com +ghostmail.com +ghosttexter.de +gigileung.org +girl4god.com +glay.org +glendale.net +globalfree.it +globalpagan.com +gmail.com +gmail.com.br +gmx.at +gmx.biz +gmx.ch +gmx.co.uk +gmx.com +gmx.de +gmx.eu +gmx.fr +gmx.info +gmx.li +gmx.net +gmx.org +gmx.us +go.com +go.ro +go.ru +gocollege.com +gocubs.com +goldmail.ru +goldtoolbox.com +golfemail.com +golfilla.info +golfmail.be +gonavy.net +goodnewsmail.com +goodstick.com +googlemail.com +goplay.com +gorillaswithdirtyarmpits.com +gospelfan.com +gotmail.com +gotmail.org +gotomy.com +gotti.otherinbox.com +gportal.hu +graduate.org +graffiti.net +gramszu.net +grandmamail.com +graphic-designer.com +grapplers.com +greenmail.net +groupmail.com +grr.la +gtmc.net +gua.net +guessmail.com +guju.net +gustr.com +guy.com +guy2.com +guyanafriends.com +h-mail.us +hab-verschlafen.de +habmalnefrage.de +hacccc.com +hackermail.com +hackermail.net +hailmail.net +hairdresser.net +hamptonroads.com +handbag.com +handleit.com +hanmail.net +happemail.com +happycounsel.com +happypuppy.com +harakirimail.com +hardcorefreak.com +hartbot.de +hawaii.rr.com +hawaiiantel.net +heerschap.com +heesun.net +hello.hu +hello.net.au +hello.to +helter-skelter.com +herediano.com +herp.in +herr-der-mails.de +hetnet.nl +hey.to +hidzz.com +highquality.com +highveldmail.co.za +hilarious.com +hiphopfan.com +hispavista.com +hitmail.com +hitthe.net +hkg.net +hockeymail.com +hollywoodkids.com +home-email.com +home.de +home.nl +home.ro +home.se +homemail.com +homestead.com +honduras.com +hongkong.com +hoopsmail.com +hopemail.biz +hot-shot.com +hot.ee +hotbrev.com +hotletter.com +hotmail.ca +hotmail.ch +hotmail.co.il +hotmail.co.uk +hotmail.com +hotmail.de +hotmail.es +hotmail.fr +hotmail.it +hotmail.kz +hotmail.nl +hotmail.ru +hotpop3.com +hotvoice.com +housemail.com +hsuchi.net +hu2.ru +hughes.net +humanoid.net +hunsa.com +hurting.com +hush.com +hushmail.com +hypernautica.com +i-connect.com +i-mail.com.au +i-p.com +i.am +i.ua +i2pmail.org +iamawoman.com +icestorm.com +ich-bin-verrueckt-nach-dir.de +ich-will-net.de +icloud.com +icmsconsultants.com +icq.com +icqmail.com +icrazy.com +idirect.com +ieh-mail.de +iespana.es +ig.com.br +ihateclowns.com +iinet.net.au +ijustdontcare.com +ikbenspamvrij.nl +ilkposta.com +ilovechocolate.com +ilovejesus.com +ilse.nl +imaginemail.com +imail.org +imail.ru +imap-mail.com +imap.cc +imapmail.org +imel.org +imgof.com +imgv.de +immo-gerance.info +imposter.co.uk +imstations.com +imstressed.com +in-box.net +iname.com +inbax.tk +inbox.com +inbox.net +inbox.ru +inbox.si +inboxalias.com +incamail.com +incredimail.com +index.ua +indexa.fr +india.com +indiatimes.com +indo-mail.com +indomail.com +indyracers.com +inerted.com +info-media.de +info-radio.ml +info66.com +infohq.com +infomail.es +infomart.or.jp +infospacemail.com +infovia.com.ar +inicia.es +inmail.sk +inmail24.com +inmano.com +inmynetwork.tk +innocent.com +inorbit.com +inoutbox.com +insidebaltimore.net +insight.rr.com +instantemailaddress.com +instantmail.fr +instruction.com +instructor.net +insurer.com +interburp.com +interfree.it +interia.pl +interlap.com.ar +intermail.co.il +internet-e-mail.com +internet-mail.org +internet-police.com +internetegypt.com +internetemails.net +internetmailing.net +internode.on.net +inwind.it +iobox.com +iobox.fi +iol.it +iowaemail.com +ip4.pp.ua +ip6.pp.ua +ipoo.org +iprimus.com.au +iqemail.com +irangate.net +ireland.com +irelandmail.com +irj.hu +iroid.com +isellcars.com +iservejesus.com +islamonline.net +isleuthmail.com +ismart.net +isp9.net +israelmail.com +ist-allein.info +ist-einmalig.de +ist-ganz-allein.de +ist-willig.de +italymail.com +itmom.com +ivebeenframed.com +iwmail.com +iwon.com +izadpanah.com +jahoopa.com +jakuza.hu +jazzandjava.com +jazzfan.com +je-recycle.info +jerusalemmail.com +jetable.de +jetable.pp.ua +jetemail.net +jippii.fi +jmail.co.za +job4u.com +jokes.com +journalist.com +jourrapide.com +jovem.te.pt +jpopmail.com +jsrsolutions.com +jubiimail.dk +juniormail.com +junkmail.com +juno.com +justemail.net +justicemail.com +kaazoo.com +kaffeeschluerfer.com +kaffeeschluerfer.de +kaixo.com +kalpoint.com +kansascity.com +karbasi.com +katamail.com +kayafmmail.co.za +kbjrmail.com +kcks.com +keg-party.com +keinpardon.de +keko.com.ar +kellychen.com +keromail.com +keyemail.com +kgb.hu +kickassmail.com +killermail.com +kimo.com +kinglibrary.net +kinki-kids.com +kissfans.com +kittymail.com +kitznet.at +kiwitown.com +km.ru +knol-power.nl +kommespaeter.de +konx.com +korea.com +koreamail.com +kpnmail.nl +krongthip.com +krunis.com +ksanmail.com +ksee24mail.com +kukamail.com +kulturbetrieb.info +kumarweb.com +la.com +ladymail.cz +lagerlouts.com +lags.us +lakmail.com +lamer.hu +land.ru +lankamail.com +laoeq.com +laposte.net +lass-es-geschehen.de +lastmail.co +latemodels.com +lavache.com +law.com +lawyer.com +lazyinbox.com +leehom.net +legalrc.loan +legislator.com +lenta.ru +leonlai.net +letsgomets.net +letterboxes.org +letthemeatspam.com +levele.hu +lex.bg +lexis-nexis-mail.com +libero.it +liberomail.com +lick101.com +liebt-dich.info +linktrader.com +linuxfreemail.com +linuxmail.org +liontrucks.com +liquidinformation.net +list.ru +listomail.com +littleapple.com +littleblueroom.com +live.at +live.ca +live.cl +live.cn +live.co.uk +live.co.za +live.com +live.com.ar +live.com.au +live.com.mx +live.com.pt +live.com.sg +live.de +live.dk +live.fr +live.ie +live.in +live.it +live.jp +live.nl +live.ru +live.se +liveradio.tk +liverpoolfans.com +llandudno.com +llangollen.com +lobbyist.com +localbar.com +locos.com +loh.pp.ua +lolfreak.net +london.com +looksmart.co.uk +looksmart.com +lopezclub.com +louiskoo.com +loveable.com +lovecat.com +lovefall.ml +lovefootball.com +lovemail.com +lover-boy.com +lovesea.gq +lovethebroncos.com +loveyouforever.de +lovingjesus.com +lowandslow.com +lroid.com +luukku.com +lycos.co.uk +lycos.com +lycos.es +lycos.ne.jp +m-hmail.com +m4.org +mac.com +macbox.com +macfreak.com +macmail.com +madonnafan.com +maennerversteherin.com +maennerversteherin.de +maffia.hu +magicmail.co.za +mail-awu.de +mail-box.cz +mail-center.com +mail-central.com +mail-easy.fr +mail-filter.com +mail-me.com +mail-page.com +mail-tester.com +mail.az +mail.be +mail.bulgaria.com +mail.by +mail.co.za +mail.com +mail.com.tr +mail.ee +mail.gr +mail.hitthebeach.com +mail.htl22.at +mail.md +mail.misterpinball.de +mail.nu +mail.org.uk +mail.pf +mail.pt +mail.ru +mail.sisna.com +mail.svenz.eu +mail.usa.com +mail.wtf +mail114.net +mail15.com +mail2007.com +mail2aaron.com +mail2abby.com +mail2abc.com +mail2actor.com +mail2admiral.com +mail2adorable.com +mail2adoration.com +mail2adore.com +mail2adventure.com +mail2aeolus.com +mail2aether.com +mail2affection.com +mail2afghanistan.com +mail2africa.com +mail2agent.com +mail2aha.com +mail2ahoy.com +mail2aim.com +mail2air.com +mail2airbag.com +mail2airforce.com +mail2airport.com +mail2alabama.com +mail2alan.com +mail2alaska.com +mail2albania.com +mail2alcoholic.com +mail2alec.com +mail2alexa.com +mail2algeria.com +mail2alicia.com +mail2alien.com +mail2allan.com +mail2allen.com +mail2allison.com +mail2alpha.com +mail2alyssa.com +mail2amanda.com +mail2amazing.com +mail2amber.com +mail2america.com +mail2american.com +mail2andorra.com +mail2andrea.com +mail2andy.com +mail2anesthesiologist.com +mail2angela.com +mail2angola.com +mail2ann.com +mail2anna.com +mail2anne.com +mail2anthony.com +mail2aphrodite.com +mail2apollo.com +mail2april.com +mail2aquarius.com +mail2arabia.com +mail2arabic.com +mail2architect.com +mail2ares.com +mail2argentina.com +mail2aries.com +mail2arizona.com +mail2arkansas.com +mail2armenia.com +mail2army.com +mail2arnold.com +mail2art.com +mail2arthur.com +mail2artist.com +mail2ashley.com +mail2ask.com +mail2astronomer.com +mail2athena.com +mail2athlete.com +mail2atlas.com +mail2atom.com +mail2attitude.com +mail2auction.com +mail2aunt.com +mail2australia.com +mail2austria.com +mail2azerbaijan.com +mail2baby.com +mail2bahamas.com +mail2bahrain.com +mail2ballerina.com +mail2ballplayer.com +mail2band.com +mail2bangladesh.com +mail2bank.com +mail2banker.com +mail2bankrupt.com +mail2baptist.com +mail2bar.com +mail2barbados.com +mail2barbara.com +mail2barter.com +mail2basketball.com +mail2batter.com +mail2beach.com +mail2beast.com +mail2beatles.com +mail2beauty.com +mail2becky.com +mail2beijing.com +mail2belgium.com +mail2belize.com +mail2ben.com +mail2bernard.com +mail2beth.com +mail2betty.com +mail2beverly.com +mail2beyond.com +mail2biker.com +mail2bill.com +mail2billionaire.com +mail2billy.com +mail2bio.com +mail2biologist.com +mail2black.com +mail2blackbelt.com +mail2blake.com +mail2blind.com +mail2blonde.com +mail2blues.com +mail2bob.com +mail2bobby.com +mail2bolivia.com +mail2bombay.com +mail2bonn.com +mail2bookmark.com +mail2boreas.com +mail2bosnia.com +mail2boston.com +mail2botswana.com +mail2bradley.com +mail2brazil.com +mail2breakfast.com +mail2brian.com +mail2bride.com +mail2brittany.com +mail2broker.com +mail2brook.com +mail2bruce.com +mail2brunei.com +mail2brunette.com +mail2brussels.com +mail2bryan.com +mail2bug.com +mail2bulgaria.com +mail2business.com +mail2buy.com +mail2ca.com +mail2california.com +mail2calvin.com +mail2cambodia.com +mail2cameroon.com +mail2canada.com +mail2cancer.com +mail2capeverde.com +mail2capricorn.com +mail2cardinal.com +mail2cardiologist.com +mail2care.com +mail2caroline.com +mail2carolyn.com +mail2casey.com +mail2cat.com +mail2caterer.com +mail2cathy.com +mail2catlover.com +mail2catwalk.com +mail2cell.com +mail2chad.com +mail2champaign.com +mail2charles.com +mail2chef.com +mail2chemist.com +mail2cherry.com +mail2chicago.com +mail2chile.com +mail2china.com +mail2chinese.com +mail2chocolate.com +mail2christian.com +mail2christie.com +mail2christmas.com +mail2christy.com +mail2chuck.com +mail2cindy.com +mail2clark.com +mail2classifieds.com +mail2claude.com +mail2cliff.com +mail2clinic.com +mail2clint.com +mail2close.com +mail2club.com +mail2coach.com +mail2coastguard.com +mail2colin.com +mail2college.com +mail2color.com +mail2colorado.com +mail2columbia.com +mail2comedian.com +mail2composer.com +mail2computer.com +mail2computers.com +mail2concert.com +mail2congo.com +mail2connect.com +mail2connecticut.com +mail2consultant.com +mail2convict.com +mail2cook.com +mail2cool.com +mail2cory.com +mail2costarica.com +mail2country.com +mail2courtney.com +mail2cowboy.com +mail2cowgirl.com +mail2craig.com +mail2crave.com +mail2crazy.com +mail2create.com +mail2croatia.com +mail2cry.com +mail2crystal.com +mail2cuba.com +mail2culture.com +mail2curt.com +mail2customs.com +mail2cute.com +mail2cutey.com +mail2cynthia.com +mail2cyprus.com +mail2czechrepublic.com +mail2dad.com +mail2dale.com +mail2dallas.com +mail2dan.com +mail2dana.com +mail2dance.com +mail2dancer.com +mail2danielle.com +mail2danny.com +mail2darlene.com +mail2darling.com +mail2darren.com +mail2daughter.com +mail2dave.com +mail2dawn.com +mail2dc.com +mail2dealer.com +mail2deanna.com +mail2dearest.com +mail2debbie.com +mail2debby.com +mail2deer.com +mail2delaware.com +mail2delicious.com +mail2demeter.com +mail2democrat.com +mail2denise.com +mail2denmark.com +mail2dennis.com +mail2dentist.com +mail2derek.com +mail2desert.com +mail2devoted.com +mail2devotion.com +mail2diamond.com +mail2diana.com +mail2diane.com +mail2diehard.com +mail2dilemma.com +mail2dillon.com +mail2dinner.com +mail2dinosaur.com +mail2dionysos.com +mail2diplomat.com +mail2director.com +mail2dirk.com +mail2disco.com +mail2dive.com +mail2diver.com +mail2divorced.com +mail2djibouti.com +mail2doctor.com +mail2doglover.com +mail2dominic.com +mail2dominica.com +mail2dominicanrepublic.com +mail2don.com +mail2donald.com +mail2donna.com +mail2doris.com +mail2dorothy.com +mail2doug.com +mail2dough.com +mail2douglas.com +mail2dow.com +mail2downtown.com +mail2dream.com +mail2dreamer.com +mail2dude.com +mail2dustin.com +mail2dyke.com +mail2dylan.com +mail2earl.com +mail2earth.com +mail2eastend.com +mail2eat.com +mail2economist.com +mail2ecuador.com +mail2eddie.com +mail2edgar.com +mail2edwin.com +mail2egypt.com +mail2electron.com +mail2eli.com +mail2elizabeth.com +mail2ellen.com +mail2elliot.com +mail2elsalvador.com +mail2elvis.com +mail2emergency.com +mail2emily.com +mail2engineer.com +mail2english.com +mail2environmentalist.com +mail2eos.com +mail2eric.com +mail2erica.com +mail2erin.com +mail2erinyes.com +mail2eris.com +mail2eritrea.com +mail2ernie.com +mail2eros.com +mail2estonia.com +mail2ethan.com +mail2ethiopia.com +mail2eu.com +mail2europe.com +mail2eurus.com +mail2eva.com +mail2evan.com +mail2evelyn.com +mail2everything.com +mail2exciting.com +mail2expert.com +mail2fairy.com +mail2faith.com +mail2fanatic.com +mail2fancy.com +mail2fantasy.com +mail2farm.com +mail2farmer.com +mail2fashion.com +mail2fat.com +mail2feeling.com +mail2female.com +mail2fever.com +mail2fighter.com +mail2fiji.com +mail2filmfestival.com +mail2films.com +mail2finance.com +mail2finland.com +mail2fireman.com +mail2firm.com +mail2fisherman.com +mail2flexible.com +mail2florence.com +mail2florida.com +mail2floyd.com +mail2fly.com +mail2fond.com +mail2fondness.com +mail2football.com +mail2footballfan.com +mail2found.com +mail2france.com +mail2frank.com +mail2frankfurt.com +mail2franklin.com +mail2fred.com +mail2freddie.com +mail2free.com +mail2freedom.com +mail2french.com +mail2freudian.com +mail2friendship.com +mail2from.com +mail2fun.com +mail2gabon.com +mail2gabriel.com +mail2gail.com +mail2galaxy.com +mail2gambia.com +mail2games.com +mail2gary.com +mail2gavin.com +mail2gemini.com +mail2gene.com +mail2genes.com +mail2geneva.com +mail2george.com +mail2georgia.com +mail2gerald.com +mail2german.com +mail2germany.com +mail2ghana.com +mail2gilbert.com +mail2gina.com +mail2girl.com +mail2glen.com +mail2gloria.com +mail2goddess.com +mail2gold.com +mail2golfclub.com +mail2golfer.com +mail2gordon.com +mail2government.com +mail2grab.com +mail2grace.com +mail2graham.com +mail2grandma.com +mail2grandpa.com +mail2grant.com +mail2greece.com +mail2green.com +mail2greg.com +mail2grenada.com +mail2gsm.com +mail2guard.com +mail2guatemala.com +mail2guy.com +mail2hades.com +mail2haiti.com +mail2hal.com +mail2handhelds.com +mail2hank.com +mail2hannah.com +mail2harold.com +mail2harry.com +mail2hawaii.com +mail2headhunter.com +mail2heal.com +mail2heather.com +mail2heaven.com +mail2hebe.com +mail2hecate.com +mail2heidi.com +mail2helen.com +mail2hell.com +mail2help.com +mail2helpdesk.com +mail2henry.com +mail2hephaestus.com +mail2hera.com +mail2hercules.com +mail2herman.com +mail2hermes.com +mail2hespera.com +mail2hestia.com +mail2highschool.com +mail2hindu.com +mail2hip.com +mail2hiphop.com +mail2holland.com +mail2holly.com +mail2hollywood.com +mail2homer.com +mail2honduras.com +mail2honey.com +mail2hongkong.com +mail2hope.com +mail2horse.com +mail2hot.com +mail2hotel.com +mail2houston.com +mail2howard.com +mail2hugh.com +mail2human.com +mail2hungary.com +mail2hungry.com +mail2hygeia.com +mail2hyperspace.com +mail2hypnos.com +mail2ian.com +mail2ice-cream.com +mail2iceland.com +mail2idaho.com +mail2idontknow.com +mail2illinois.com +mail2imam.com +mail2in.com +mail2india.com +mail2indian.com +mail2indiana.com +mail2indonesia.com +mail2infinity.com +mail2intense.com +mail2iowa.com +mail2iran.com +mail2iraq.com +mail2ireland.com +mail2irene.com +mail2iris.com +mail2irresistible.com +mail2irving.com +mail2irwin.com +mail2isaac.com +mail2israel.com +mail2italian.com +mail2italy.com +mail2jackie.com +mail2jacob.com +mail2jail.com +mail2jaime.com +mail2jake.com +mail2jamaica.com +mail2james.com +mail2jamie.com +mail2jan.com +mail2jane.com +mail2janet.com +mail2janice.com +mail2japan.com +mail2japanese.com +mail2jasmine.com +mail2jason.com +mail2java.com +mail2jay.com +mail2jazz.com +mail2jed.com +mail2jeffrey.com +mail2jennifer.com +mail2jenny.com +mail2jeremy.com +mail2jerry.com +mail2jessica.com +mail2jessie.com +mail2jesus.com +mail2jew.com +mail2jeweler.com +mail2jim.com +mail2jimmy.com +mail2joan.com +mail2joann.com +mail2joanna.com +mail2jody.com +mail2joe.com +mail2joel.com +mail2joey.com +mail2john.com +mail2join.com +mail2jon.com +mail2jonathan.com +mail2jones.com +mail2jordan.com +mail2joseph.com +mail2josh.com +mail2joy.com +mail2juan.com +mail2judge.com +mail2judy.com +mail2juggler.com +mail2julian.com +mail2julie.com +mail2jumbo.com +mail2junk.com +mail2justin.com +mail2justme.com +mail2k.ru +mail2kansas.com +mail2karate.com +mail2karen.com +mail2karl.com +mail2karma.com +mail2kathleen.com +mail2kathy.com +mail2katie.com +mail2kay.com +mail2kazakhstan.com +mail2keen.com +mail2keith.com +mail2kelly.com +mail2kelsey.com +mail2ken.com +mail2kendall.com +mail2kennedy.com +mail2kenneth.com +mail2kenny.com +mail2kentucky.com +mail2kenya.com +mail2kerry.com +mail2kevin.com +mail2kim.com +mail2kimberly.com +mail2king.com +mail2kirk.com +mail2kiss.com +mail2kosher.com +mail2kristin.com +mail2kurt.com +mail2kuwait.com +mail2kyle.com +mail2kyrgyzstan.com +mail2la.com +mail2lacrosse.com +mail2lance.com +mail2lao.com +mail2larry.com +mail2latvia.com +mail2laugh.com +mail2laura.com +mail2lauren.com +mail2laurie.com +mail2lawrence.com +mail2lawyer.com +mail2lebanon.com +mail2lee.com +mail2leo.com +mail2leon.com +mail2leonard.com +mail2leone.com +mail2leslie.com +mail2letter.com +mail2liberia.com +mail2libertarian.com +mail2libra.com +mail2libya.com +mail2liechtenstein.com +mail2life.com +mail2linda.com +mail2linux.com +mail2lionel.com +mail2lipstick.com +mail2liquid.com +mail2lisa.com +mail2lithuania.com +mail2litigator.com +mail2liz.com +mail2lloyd.com +mail2lois.com +mail2lola.com +mail2london.com +mail2looking.com +mail2lori.com +mail2lost.com +mail2lou.com +mail2louis.com +mail2louisiana.com +mail2lovable.com +mail2love.com +mail2lucky.com +mail2lucy.com +mail2lunch.com +mail2lust.com +mail2luxembourg.com +mail2luxury.com +mail2lyle.com +mail2lynn.com +mail2madagascar.com +mail2madison.com +mail2madrid.com +mail2maggie.com +mail2mail4.com +mail2maine.com +mail2malawi.com +mail2malaysia.com +mail2maldives.com +mail2mali.com +mail2malta.com +mail2mambo.com +mail2man.com +mail2mandy.com +mail2manhunter.com +mail2mankind.com +mail2many.com +mail2marc.com +mail2marcia.com +mail2margaret.com +mail2margie.com +mail2marhaba.com +mail2maria.com +mail2marilyn.com +mail2marines.com +mail2mark.com +mail2marriage.com +mail2married.com +mail2marries.com +mail2mars.com +mail2marsha.com +mail2marshallislands.com +mail2martha.com +mail2martin.com +mail2marty.com +mail2marvin.com +mail2mary.com +mail2maryland.com +mail2mason.com +mail2massachusetts.com +mail2matt.com +mail2matthew.com +mail2maurice.com +mail2mauritania.com +mail2mauritius.com +mail2max.com +mail2maxwell.com +mail2maybe.com +mail2mba.com +mail2me4u.com +mail2mechanic.com +mail2medieval.com +mail2megan.com +mail2mel.com +mail2melanie.com +mail2melissa.com +mail2melody.com +mail2member.com +mail2memphis.com +mail2methodist.com +mail2mexican.com +mail2mexico.com +mail2mgz.com +mail2miami.com +mail2michael.com +mail2michelle.com +mail2michigan.com +mail2mike.com +mail2milan.com +mail2milano.com +mail2mildred.com +mail2milkyway.com +mail2millennium.com +mail2millionaire.com +mail2milton.com +mail2mime.com +mail2mindreader.com +mail2mini.com +mail2minister.com +mail2minneapolis.com +mail2minnesota.com +mail2miracle.com +mail2missionary.com +mail2mississippi.com +mail2missouri.com +mail2mitch.com +mail2model.com +mail2mom.com +mail2monaco.com +mail2money.com +mail2mongolia.com +mail2monica.com +mail2montana.com +mail2monty.com +mail2moon.com +mail2morocco.com +mail2morpheus.com +mail2mors.com +mail2moscow.com +mail2moslem.com +mail2mouseketeer.com +mail2movies.com +mail2mozambique.com +mail2mp3.com +mail2mrright.com +mail2msright.com +mail2museum.com +mail2music.com +mail2musician.com +mail2muslim.com +mail2my.com +mail2myboat.com +mail2mycar.com +mail2mycell.com +mail2mygsm.com +mail2mylaptop.com +mail2mymac.com +mail2mypager.com +mail2mypalm.com +mail2mypc.com +mail2myphone.com +mail2myplane.com +mail2namibia.com +mail2nancy.com +mail2nasdaq.com +mail2nathan.com +mail2nauru.com +mail2navy.com +mail2neal.com +mail2nebraska.com +mail2ned.com +mail2neil.com +mail2nelson.com +mail2nemesis.com +mail2nepal.com +mail2netherlands.com +mail2network.com +mail2nevada.com +mail2newhampshire.com +mail2newjersey.com +mail2newmexico.com +mail2newyork.com +mail2newzealand.com +mail2nicaragua.com +mail2nick.com +mail2nicole.com +mail2niger.com +mail2nigeria.com +mail2nike.com +mail2no.com +mail2noah.com +mail2noel.com +mail2noelle.com +mail2normal.com +mail2norman.com +mail2northamerica.com +mail2northcarolina.com +mail2northdakota.com +mail2northpole.com +mail2norway.com +mail2notus.com +mail2noway.com +mail2nowhere.com +mail2nuclear.com +mail2nun.com +mail2ny.com +mail2oasis.com +mail2oceanographer.com +mail2ohio.com +mail2ok.com +mail2oklahoma.com +mail2oliver.com +mail2oman.com +mail2one.com +mail2onfire.com +mail2online.com +mail2oops.com +mail2open.com +mail2ophthalmologist.com +mail2optometrist.com +mail2oregon.com +mail2oscars.com +mail2oslo.com +mail2painter.com +mail2pakistan.com +mail2pan.com +mail2panama.com +mail2paraguay.com +mail2paralegal.com +mail2paris.com +mail2park.com +mail2parker.com +mail2party.com +mail2passion.com +mail2pat.com +mail2patricia.com +mail2patrick.com +mail2patty.com +mail2paul.com +mail2paula.com +mail2pay.com +mail2peace.com +mail2pediatrician.com +mail2peggy.com +mail2pennsylvania.com +mail2perry.com +mail2persephone.com +mail2persian.com +mail2peru.com +mail2pete.com +mail2peter.com +mail2pharmacist.com +mail2phil.com +mail2philippines.com +mail2phoenix.com +mail2phonecall.com +mail2phyllis.com +mail2pickup.com +mail2pilot.com +mail2pisces.com +mail2planet.com +mail2platinum.com +mail2plato.com +mail2pluto.com +mail2pm.com +mail2podiatrist.com +mail2poet.com +mail2poland.com +mail2policeman.com +mail2policewoman.com +mail2politician.com +mail2pop.com +mail2pope.com +mail2popular.com +mail2portugal.com +mail2poseidon.com +mail2potatohead.com +mail2power.com +mail2presbyterian.com +mail2president.com +mail2priest.com +mail2prince.com +mail2princess.com +mail2producer.com +mail2professor.com +mail2protect.com +mail2psychiatrist.com +mail2psycho.com +mail2psychologist.com +mail2qatar.com +mail2queen.com +mail2rabbi.com +mail2race.com +mail2racer.com +mail2rachel.com +mail2rage.com +mail2rainmaker.com +mail2ralph.com +mail2randy.com +mail2rap.com +mail2rare.com +mail2rave.com +mail2ray.com +mail2raymond.com +mail2realtor.com +mail2rebecca.com +mail2recruiter.com +mail2recycle.com +mail2redhead.com +mail2reed.com +mail2reggie.com +mail2register.com +mail2rent.com +mail2republican.com +mail2resort.com +mail2rex.com +mail2rhodeisland.com +mail2rich.com +mail2richard.com +mail2ricky.com +mail2ride.com +mail2riley.com +mail2rita.com +mail2rob.com +mail2robert.com +mail2roberta.com +mail2robin.com +mail2rock.com +mail2rocker.com +mail2rod.com +mail2rodney.com +mail2romania.com +mail2rome.com +mail2ron.com +mail2ronald.com +mail2ronnie.com +mail2rose.com +mail2rosie.com +mail2roy.com +mail2rss.org +mail2rudy.com +mail2rugby.com +mail2runner.com +mail2russell.com +mail2russia.com +mail2russian.com +mail2rusty.com +mail2ruth.com +mail2rwanda.com +mail2ryan.com +mail2sa.com +mail2sabrina.com +mail2safe.com +mail2sagittarius.com +mail2sail.com +mail2sailor.com +mail2sal.com +mail2salaam.com +mail2sam.com +mail2samantha.com +mail2samoa.com +mail2samurai.com +mail2sandra.com +mail2sandy.com +mail2sanfrancisco.com +mail2sanmarino.com +mail2santa.com +mail2sara.com +mail2sarah.com +mail2sat.com +mail2saturn.com +mail2saudi.com +mail2saudiarabia.com +mail2save.com +mail2savings.com +mail2school.com +mail2scientist.com +mail2scorpio.com +mail2scott.com +mail2sean.com +mail2search.com +mail2seattle.com +mail2secretagent.com +mail2senate.com +mail2senegal.com +mail2sensual.com +mail2seth.com +mail2sevenseas.com +mail2sexy.com +mail2seychelles.com +mail2shane.com +mail2sharon.com +mail2shawn.com +mail2ship.com +mail2shirley.com +mail2shoot.com +mail2shuttle.com +mail2sierraleone.com +mail2simon.com +mail2singapore.com +mail2single.com +mail2site.com +mail2skater.com +mail2skier.com +mail2sky.com +mail2sleek.com +mail2slim.com +mail2slovakia.com +mail2slovenia.com +mail2smile.com +mail2smith.com +mail2smooth.com +mail2soccer.com +mail2soccerfan.com +mail2socialist.com +mail2soldier.com +mail2somalia.com +mail2son.com +mail2song.com +mail2sos.com +mail2sound.com +mail2southafrica.com +mail2southamerica.com +mail2southcarolina.com +mail2southdakota.com +mail2southkorea.com +mail2southpole.com +mail2spain.com +mail2spanish.com +mail2spectrum.com +mail2splash.com +mail2sponsor.com +mail2sports.com +mail2srilanka.com +mail2stacy.com +mail2stan.com +mail2stanley.com +mail2star.com +mail2state.com +mail2stephanie.com +mail2steve.com +mail2steven.com +mail2stewart.com +mail2stlouis.com +mail2stock.com +mail2stockholm.com +mail2stockmarket.com +mail2storage.com +mail2store.com +mail2strong.com +mail2student.com +mail2studio.com +mail2studio54.com +mail2stuntman.com +mail2subscribe.com +mail2sudan.com +mail2superstar.com +mail2surfer.com +mail2suriname.com +mail2susan.com +mail2suzie.com +mail2swaziland.com +mail2sweden.com +mail2sweetheart.com +mail2swim.com +mail2swimmer.com +mail2swiss.com +mail2switzerland.com +mail2sydney.com +mail2sylvia.com +mail2syria.com +mail2taboo.com +mail2taiwan.com +mail2tajikistan.com +mail2tammy.com +mail2tango.com +mail2tanya.com +mail2tanzania.com +mail2tara.com +mail2taurus.com +mail2taxi.com +mail2taxidermist.com +mail2taylor.com +mail2taz.com +mail2teacher.com +mail2technician.com +mail2ted.com +mail2telephone.com +mail2tenderness.com +mail2tennessee.com +mail2tennis.com +mail2tennisfan.com +mail2terri.com +mail2terry.com +mail2test.com +mail2texas.com +mail2thailand.com +mail2therapy.com +mail2think.com +mail2tickets.com +mail2tiffany.com +mail2tim.com +mail2time.com +mail2timothy.com +mail2tina.com +mail2titanic.com +mail2toby.com +mail2todd.com +mail2togo.com +mail2tom.com +mail2tommy.com +mail2tonga.com +mail2tony.com +mail2touch.com +mail2tourist.com +mail2tracey.com +mail2tracy.com +mail2tramp.com +mail2travel.com +mail2traveler.com +mail2travis.com +mail2trekkie.com +mail2trex.com +mail2triallawyer.com +mail2trick.com +mail2trillionaire.com +mail2troy.com +mail2truck.com +mail2trump.com +mail2try.com +mail2tunisia.com +mail2turbo.com +mail2turkey.com +mail2turkmenistan.com +mail2tv.com +mail2tycoon.com +mail2tyler.com +mail2u4me.com +mail2uae.com +mail2uganda.com +mail2uk.com +mail2ukraine.com +mail2uncle.com +mail2unsubscribe.com +mail2uptown.com +mail2uruguay.com +mail2usa.com +mail2utah.com +mail2uzbekistan.com +mail2v.com +mail2vacation.com +mail2valentines.com +mail2valerie.com +mail2valley.com +mail2vamoose.com +mail2vanessa.com +mail2vanuatu.com +mail2venezuela.com +mail2venous.com +mail2venus.com +mail2vermont.com +mail2vickie.com +mail2victor.com +mail2victoria.com +mail2vienna.com +mail2vietnam.com +mail2vince.com +mail2virginia.com +mail2virgo.com +mail2visionary.com +mail2vodka.com +mail2volleyball.com +mail2waiter.com +mail2wallstreet.com +mail2wally.com +mail2walter.com +mail2warren.com +mail2washington.com +mail2wave.com +mail2way.com +mail2waycool.com +mail2wayne.com +mail2webmaster.com +mail2webtop.com +mail2webtv.com +mail2weird.com +mail2wendell.com +mail2wendy.com +mail2westend.com +mail2westvirginia.com +mail2whether.com +mail2whip.com +mail2white.com +mail2whitehouse.com +mail2whitney.com +mail2why.com +mail2wilbur.com +mail2wild.com +mail2willard.com +mail2willie.com +mail2wine.com +mail2winner.com +mail2wired.com +mail2wisconsin.com +mail2woman.com +mail2wonder.com +mail2world.com +mail2worship.com +mail2wow.com +mail2www.com +mail2wyoming.com +mail2xfiles.com +mail2xox.com +mail2yachtclub.com +mail2yahalla.com +mail2yemen.com +mail2yes.com +mail2yugoslavia.com +mail2zack.com +mail2zambia.com +mail2zenith.com +mail2zephir.com +mail2zeus.com +mail2zipper.com +mail2zoo.com +mail2zoologist.com +mail2zurich.com +mail3000.com +mail4trash.com +mail4u.info +mailandftp.com +mailas.com +mailasia.com +mailbolt.com +mailboom.com +mailbox.as +mailbox.co.za +mailbox.gr +mailbox.hu +mailc.net +mailcan.com +mailcat.biz +mailcc.com +mailcity.com +mailclub.fr +maildx.com +mailed.ro +mailexcite.com +mailfa.tk +mailforce.net +mailforspam.com +mailfs.com +mailftp.com +mailgenie.net +mailguard.me +mailhaven.com +mailhood.com +mailimate.com +mailinatar.com +mailinator.org +mailinblack.com +mailingaddress.org +mailingweb.com +mailisent.com +mailismagic.com +mailite.com +mailmate.com +mailme.dk +mailme.gq +mailme24.com +mailmight.com +mailnator.com +mailnew.com +mailoye.com +mailpanda.com +mailpick.biz +mailpost.zzn.com +mailpride.com +mailproxsy.com +mailpuppy.com +mailquack.com +mailrock.biz +mailroom.com +mailru.com +mailsac.com +mailsent.net +mailservice.ms +mailshuttle.com +mailslapping.com +mailstart.com +mailstartplus.com +mailsurf.com +mailtag.com +mailtemp.info +mailtothis.com +mailueberfall.de +mailup.net +mailwire.com +mailworks.org +mailzi.ru +mailzilla.org +malayalamtelevision.net +maltesemail.com +mamber.net +manager.de +mancity.net +mantramail.com +manybrain.com +marchmail.com +mariahc.com +marijuana.com +marijuana.nl +married-not.com +martindalemail.com +masrawy.com +matmail.com +mauimail.com +mauritius.com +maxmail.co.uk +mbox.com.au +me.com +meta.ua +medical.net.au +medscape.com +meetingmall.com +megapoint.com +mehrani.com +mehtaweb.com +meine-dateien.info +meine-diashow.de +meine-fotos.info +meine-urlaubsfotos.de +mekhong.com +merda.flu.cc +merda.igg.biz +merda.nut.cc +merda.usa.cc +message.hu +messages.to +metacrawler.com +metalfan.com +metta.lk +mexicomail.com +mezimages.net +mfsa.ru +mierdamail.com +miesto.sk +mighty.co.za +migmail.net +migmail.pl +miho-nakayama.com +mikrotamanet.com +millionaireintraining.com +millionairemail.com +milmail.com +mindless.com +mindspring.com +minister.com +misery.net +mittalweb.com +mixmail.com +mjfrogmail.com +ml1.net +mm.st +mns.ru +moakt.com +mobileninja.co.uk +mochamail.com +mohammed.com +mohmal.com +moldova.cc +moldova.com +moldovacc.com +momslife.com +monemail.com +money.net +montevideo.com.uy +monumentmail.com +moose-mail.com +mor19.uu.gl +mortaza.com +moscowmail.com +mostlysunny.com +motormania.com +movemail.com +movieluver.com +mox.pp.ua +mp4.it +mr-potatohead.com +msgbox.com +msn.cn +msn.com +msn.nl +mt2015.com +mt2016.com +mttestdriver.com +muehlacker.tk +munich.com +music.com +musician.org +musicscene.org +muskelshirt.de +muslim.com +muslimsonline.com +mutantweb.com +mvrht.com +my.com +my10minutemail.com +mybox.it +mycity.com +mydomain.com +mydotcomaddress.com +myfamily.com +myfastmail.com +mygo.com +myiris.com +mymacmail.com +mynamedot.com +mynet.com +mynetstore.de +myownemail.com +mypacks.net +mypad.com +myplace.com +myrambler.ru +myrealbox.com +myremarq.com +myself.com +myspamless.com +mystupidjob.com +mytemp.email +mythirdage.com +myway.com +myworldmail.com +n2.com +n2baseball.com +n2mail.com +n2soccer.com +n2software.com +nabc.biz +nafe.com +nakedgreens.com +name.com +naplesnews.net +naseej.com +nativeweb.net +naui.net +naver.com +navigator.lv +navy.org +naz.com +nchoicemail.com +neeva.net +nenter.com +neo.rr.com +nervhq.org +net-c.be +net-c.ca +net-c.cat +net-c.com +net-c.es +net-c.fr +net-c.it +net-c.lu +net-c.nl +net-c.pl +net-pager.net +net-shopping.com +net4b.pt +net4you.at +netbounce.com +netbroadcaster.com +netby.dk +netc.eu +netc.fr +netc.it +netc.lu +netc.pl +netcenter-vn.net +netcmail.com +netcourrier.com +netexecutive.com +netexpressway.com +netgenie.com +netian.com +netizen.com.ar +netmongol.com +netnoir.net +netpiper.com +netralink.com +netscape.net +netspace.net.au +netster.com +nettaxi.com +nettemail.com +netterchef.de +netzero.com +netzero.net +neue-dateien.de +neuf.fr +neuro.md +newmail.com +newmail.net +newmail.ru +newsboysmail.com +newyork.com +nextmail.ru +nexxmail.com +nfmail.com +nicebush.com +nicegal.com +nicholastse.net +nicolastse.com +nikopage.com +nimail.com +ninfan.com +nirvanafan.com +nmail.cf +noavar.com +nonpartisan.com +nonspam.eu +nonspammer.de +norika-fujiwara.com +norikomail.com +northgates.net +nowhere.org +ntlhelp.net +ntscan.com +null.net +nullbox.info +nur-fuer-spam.de +nus.edu.sg +nwldx.com +nxt.ru +ny.com +nybella.com +nyc.com +nycmail.com +nzoomail.com +o-tay.com +o2.co.uk +oaklandas-fan.com +oath.com +oceanfree.net +oddpost.com +odmail.com +office-dateien.de +office-email.com +offroadwarrior.com +oicexchange.com +oikrach.com +okbank.com +okhuman.com +okmagic.com +oldies104mail.com +olemail.com +olympist.net +olypmall.ru +omaninfo.com +omen.ru +onebox.com +onenet.com.ar +oneoffmail.com +onet.com.pl +onet.eu +onet.pl +oninet.pt +online.ie +online.ms +online.nl +onlinewiz.com +onmilwaukee.com +onobox.com +op.pl +opayq.com +openmailbox.org +operafan.com +operamail.com +opoczta.pl +optician.com +optonline.net +optusnet.com.au +orange.fr +orbitel.bg +orgmail.net +orthodontist.net +osite.com.br +oso.com +otakumail.com +our-computer.com +our-office.com +our.st +ourbrisbane.com +ournet.md +outgun.com +outlook.at +outlook.be +outlook.cl +outlook.co.id +outlook.co.il +outlook.co.nz +outlook.co.th +outlook.com +outlook.com.au +outlook.com.br +outlook.com.gr +outlook.com.pe +outlook.com.tr +outlook.com.vn +outlook.cz +outlook.de +outlook.dk +outlook.es +outlook.fr +outlook.hu +outlook.ie +outlook.in +outlook.it +outlook.jp +outlook.kr +outlook.lv +outlook.my +outlook.ph +outlook.pt +outlook.sa +outlook.sg +outlook.sk +over-the-rainbow.com +ownmail.net +ozbytes.net.au +ozemail.com.au +pacbell.net +pacific-ocean.com +pacific-re.com +pacificwest.com +pagina.de +pagons.org +pakistanmail.com +pakistanoye.com +parkjiyoon.com +parrot.com +parsmail.com +partlycloudy.com +partybombe.de +partyheld.de +partynight.at +passwordmail.com +pathfindermail.com +pcusers.otherinbox.com +pediatrician.com +penpen.com +peoplepc.com +peopleweb.com +pepbot.com +perfectmail.com +perso.be +personal.ro +personales.com +petlover.com +petml.com +pettypool.com +pezeshkpour.com +pfui.ru +phayze.com +phone.net +photographer.net +phpbb.uu.gl +phreaker.net +physicist.net +pianomail.com +pickupman.com +picusnet.com +pigpig.net +pinoymail.com +piracha.net +pisem.net +pjjkp.com +planet.nl +planetaccess.com +planetarymotion.net +planetearthinter.net +planetmail.com +planetmail.net +planetout.com +playersodds.com +playful.com +plus.com +plusmail.com.br +pmail.net +pobox.sk +pochta.ru +poczta.fm +poczta.onet.pl +poetic.com +pokemail.net +pokemonpost.com +pokepost.com +polandmail.com +polbox.com +politician.com +polizisten-duzer.de +poond.com +popaccount.com +popmail.com +popsmail.com +popstar.com +portugalmail.com +portugalmail.pt +post.com +post.cz +post.sk +posta.ro +postaccesslite.com +postafree.com +postfach.cc +postinbox.com +postino.ch +postmark.net +postmaster.co.uk +postpro.net +powerfan.com +praize.com +premiumservice.com +presidency.com +priest.com +primposta.com +primposta.hu +privy-mail.com +privymail.de +pro.hu +probemail.com +prodigy.net +progetplus.it +programist.ru +programmer.net +proinbox.com +promessage.com +prontomail.com +protestant.com +protonmail.com +prydirect.info +psv-supporter.com +ptd.net +public-files.de +public.usa.com +publicist.com +pulp-fiction.com +purpleturtle.com +put2.net +pwrby.com +q.com +qmail.com +qprfans.com +qq.com +quackquack.com +quakemail.com +qualityservice.com +quantentunnel.de +quickhosts.com +quickmail.nl +quicknet.nl +quickwebmail.com +quiklinks.com +quikmail.com +qv7.info +qwest.net +qwestoffice.net +racedriver.com +racefanz.com +racingmail.com +radicalz.com +radiku.ye.vc +radiologist.net +ragingbull.com +ralib.com +rambler.ru +rambler.ua +ranmamail.com +rastogi.net +ratt-n-roll.com +rattle-snake.com +raubtierbaendiger.de +ravearena.com +ravemail.com +realemail.net +reality-concept.club +reallyfast.biz +reallyfast.info +reallymymail.com +realtyagent.com +reborn.com +reconmail.com +recycler.com +recyclermail.com +rediff.com +rediffmail.com +rediffmailpro.com +rednecks.com +redseven.de +reggaefan.com +registerednurses.com +regspaces.tk +reincarnate.com +religious.com +remail.ga +renren.com +repairman.com +reply.hu +representative.com +rescueteam.com +resgedvgfed.tk +resumemail.com +rezai.com +rhyta.com +richmondhill.com +rickymail.com +rin.ru +riopreto.com.br +rn.com +ro.ru +roadrunner.com +roanokemail.com +rock.com +rocketmail.com +rocketship.com +rockfan.com +rodrun.com +rogers.com +roosh.com +rootprompt.org +royal.net +rr.com +rrohio.com +rsub.com +runbox.com +rushpost.com +ruttolibero.com +rvshop.com +s-mail.com +sacbeemail.com +saeuferleber.de +safrica.com +sagra.lu +sags-per-mail.de +sailormoon.com +saintly.com +salehi.net +salesperson.net +samerica.com +samilan.net +sammimail.com +sandelf.de +sanfranmail.com +sanook.com +sapo.pt +saudia.com +sayhi.net +sbcglobal.net +scandalmail.com +scarlet.nl +schafmail.de +schizo.com +schmusemail.de +schoolmail.com +schoolsucks.com +schreib-doch-mal-wieder.de +sci.fi +scientist.com +scotland.com +scotlandmail.com +scottishmail.co.uk +scubadiving.com +seanet.com +search.ua +searchwales.com +sebil.com +secret-police.com +secretary.net +secretservices.net +secure-mail.biz +secure-mail.cc +seductive.com +seekstoyboy.com +seguros.com.br +selfdestructingmail.com +send.hu +sendme.cz +sendspamhere.com +sent.as +sent.at +sent.com +sentrismail.com +serga.com.ar +servemymail.com +servermaps.net +sesmail.com +sexmagnet.com +seznam.cz +sfr.fr +shaniastuff.com +shared-files.de +sharedmailbox.org +sharmaweb.com +she.com +shieldedmail.com +shinedyoureyes.com +shitaway.cf +shitaway.ga +shitaway.gq +shitaway.ml +shitaway.tk +shitaway.usa.cc +shitmail.de +shitmail.org +shitware.nl +shortmail.com +shotgun.hu +showslow.de +sialkotcity.com +sialkotian.com +sialkotoye.com +sify.com +silkroad.net +sina.cn +sina.com +singles4jesus.com +singmail.com +singnet.com.sg +singpost.com +sinnlos-mail.de +siteposter.net +skafan.com +skeefmail.com +skim.com +skizo.hu +skrx.tk +sky.com +slamdunkfan.com +slave-auctions.net +slingshot.com +slippery.email +slipry.net +slotter.com +smap.4nmv.ru +smapxsmap.net +smashmail.de +smoothmail.com +sms.at +snail-mail.net +snakebite.com +snet.net +sniper.hu +snkmail.com +snoopymail.com +snowboarding.com +snowdonia.net +socceramerica.net +soccermail.com +soccermomz.com +social-mailer.tk +socialworker.net +sociologist.com +sofort-mail.de +sofortmail.de +softhome.net +sogou.com +sohu.com +sol.dk +solcon.nl +soldier.hu +solution4u.com +solvemail.info +songwriter.net +sonnenkinder.org +soodomail.com +soon.com +soulfoodcookbook.com +sp.nl +space-bank.com +space-man.com +space-ship.com +space-travel.com +space.com +spacemart.com +spacewar.com +spainmail.com +spam.2012-2016.ru +spamavert.com +spambob.com +spambooger.com +spamdecoy.net +spameater.com +spamfree24.info +spaminator.de +spaml.com +spamoff.de +spartapiet.com +speedemail.net +speedpost.net +speedrules.com +speedrulz.com +speedymail.org +sperke.net +spils.com +spinfinder.com +spl.at +spoko.pl +spoofmail.de +sportsmail.com +sporttruckdriver.com +spray.se +spybox.de +spymac.com +srilankan.net +ssl-mail.com +st-davids.net +stade.fr +stargateradio.com +starmail.com +starmedia.com +starspath.com +start.com.au +startkeys.com +stinkefinger.net +stipte.nl +stoned.com +stones.com +stop-my-spam.pp.ua +streber24.de +streetwisemail.com +strompost.com +strongguy.com +student.su +studentcenter.org +stuffmail.de +subram.com +sudolife.me +sudolife.net +sudomail.biz +sudomail.com +sudomail.net +sudoverse.com +sudoverse.net +sudoweb.net +sudoworld.com +sudoworld.net +suhabi.com +sukhumvit.net +sunpoint.net +sunrise-sunset.com +sunsgame.com +sunumail.sn +superdada.com +supereva.it +supermail.ru +superrito.com +surf3.net +surfree.com +surfy.net +surgical.net +surimail.com +survivormail.com +svk.jp +swbell.net +sweb.cz +swedenmail.com +sweetxxx.de +swift-mail.com +swiftdesk.com +swingeasyhithard.com +swingfan.com +swipermail.zzn.com +swirve.com +swissmail.com +swissmail.net +switchboardmail.com +sx172.com +syom.com +t-online.de +t.psh.me +t2mail.com +tafmail.com +takuyakimura.com +talk21.com +talkinator.com +tamil.com +tampabay.rr.com +tankpolice.com +tatanova.com +tbwt.com +tds.net +teachermail.net +teachers.org +teamdiscovery.com +teamtulsa.net +tech-center.com +tech4peace.org +techemail.com +techie.com +technisamail.co.za +technologist.com +techscout.com +techspot.com +tele2.at +tele2.nl +teleline.es +telerymd.com +teleworm.us +telfort.nl +telfortglasvezel.nl +telinco.net +telpage.net +telstra.com +telstra.com.au +temp-mail.com +temp-mail.de +temp.headstrong.de +tempail.com +tempemail.biz +tempmail.us +tempmaildemo.com +tempmailer.com +temporarioemail.com.br +temporaryemail.us +tempthe.net +tempymail.com +temtulsa.net +tenchiclub.com +tenderkiss.com +tennismail.com +terminverpennt.de +terra.cl +terra.com +terra.com.ar +terra.com.br +test.com +test.de +tfanus.com.er +tfz.net +thai.com +thaimail.com +thaimail.net +thanksnospam.info +the-african.com +the-aliens.com +the-american.com +the-animal.com +the-astronaut.com +the-beauty.com +the-big-apple.com +the-boss.com +the-captain.com +the-cowboy.com +the-eagles.com +the-fastest.net +the-galaxy.net +the-genius.com +the-gentleman.com +the-german.com +the-italian.com +the-lair.com +the-madman.com +the-marine.com +the-master.com +the-mexican.com +the-monkey.com +the-pentagon.com +the-professional.com +the-quickest.com +the-russian.com +the-spaceman.com +thecriminals.com +thedoghousemail.com +thedorm.com +theend.hu +theglobe.com +thegolfcourse.com +theheadoffice.com +theinternetemail.com +thelanddownunder.com +themail.com +themillionare.net +theplate.com +thepokerface.com +thepostmaster.net +theraces.com +therapist.net +thestreetfighter.com +thewatercooler.com +thewebpros.co.uk +thirdage.com +thisgirl.com +thraml.com +throwam.com +tidni.com +tiscali.co.uk +tiscali.it +tkcity.com +tmail.ws +toast.com +toke.com +tom.com +toolsource.com +toomail.biz +toothfairy.com +topletter.com +topmail-files.de +torontomail.com +tortenboxer.de +totalmail.de +totalmusic.net +tpg.com.au +trash-mail.ml +trashdevil.de +trashymail.net +trayna.com +trialbytrivia.com +trickmail.net +trimix.cn +tritium.net +trmailbox.com +tropicalstorm.com +truckracer.com +truckracers.com +truthmail.com +tsamail.co.za +ttml.co.in +turboprinz.de +turboprinzessin.de +turkey.com +tut.by +tvstar.com +twc.com +twinstarsmail.com +typemail.com +u2club.com +ua.fm +ubbi.com +uboot.com +uk2.net +uk2k.com +uk2net.com +uk7.net +uk8.net +ukbuilder.com +ukcool.com +ukdreamcast.com +ukmail.org +ukmax.com +ukr.net +uku.co.uk +ultapulta.com +ultrapostman.com +ummah.org +umpire.com +unbounded.com +unforgettable.com +uni.de +unican.es +unihome.com +universal.pt +uno.ee +uno.it +unofree.it +unterderbruecke.de +uol.com.br +uol.com.co +uol.com.ve +uomail.com +upc.nl +upcmail.nl +upf.org +uplipht.com +ureach.com +uroid.com +usa.com +usa.net +usaaccess.net +usermail.com +username.e4ward.com +usma.net +usmc.net +uswestmail.net +utanet.at +uymail.com +uyuyuy.com +vaasfc4.tk +vahoo.com +valemail.net +vampirehunter.com +varbizmail.com +vcmail.com +velnet.co.uk +velocall.com +verizon.net +verlass-mich-nicht.de +versatel.nl +veryfast.biz +veryrealemail.com +veryspeedy.net +vfemail.net +vickaentb.tk +videotron.ca +viditag.com +vinbazar.com +violinmakers.co.uk +vip.126.com +vip.163.com +vip.21cn.com +vip.citiz.net +vip.gr +vip.onet.pl +vip.qq.com +vip.sina.com +vipmail.ru +virgilio.it +virgin.net +virginbroadband.com.au +visitweb.com +visto.com +vivavelocity.com +vivianhsu.net +vkcode.ru +vnet.citiz.net +vnn.vn +vodafone.nl +vodafonethuis.nl +volcanomail.com +vollbio.de +volloeko.de +vomoto.com +vorsicht-bissig.de +vorsicht-scharf.de +vote-democrats.com +vote-republicans.com +vote4gop.org +votenet.com +vp.pl +vr9.com +vubby.com +w3.to +wahoye.com +walala.org +wales2000.net +walkmail.net +walkmail.ru +wam.co.za +wanadoo.es +wanadoo.fr +war-im-urlaub.de +warmmail.com +warpmail.net +warrior.hu +wazabi.club +wbdet.com +web-contact.info +web-emailbox.eu +web-mail.com.ar +web-mail.pp.ua +web-police.com +web.de +webave.com +webcity.ca +webcontact-france.eu +webdream.com +webindia123.com +webmail.co.za +webmail.hu +webmails.com +webname.com +webstation.com +websurfer.co.za +webtopmail.com +wee.my +weekonline.com +wefjo.grn.cc +weg-werf-email.de +wegas.ru +wegwerf-emails.de +wegwerfmail.info +wegwerpmailadres.nl +wehshee.com +weibsvolk.de +weibsvolk.org +weinenvorglueck.de +welsh-lady.com +westnet.com.au +wfgdfhj.tk +whale-mail.com +whartontx.com +whatiaas.com +whatpaas.com +wheelweb.com +whipmail.com +whoever.com +whtjddn.33mail.com +wickmail.net +wideopenwest.com +wildmail.com +wilemail.com +will-hier-weg.de +windowslive.com +windstream.net +wingnutz.com +winning.com +wir-haben-nachwuchs.de +wir-sind-cool.org +witty.com +wiz.cc +wkbwmail.com +wmail.cf +wo.com.cn +woh.rr.com +wolke7.net +wombles.com +women-at-work.org +wongfaye.com +wooow.it +worker.com +workmail.com +worldemail.com +worldnet.att.net +wormseo.cn +wosaddict.com +wowgirl.com +wowmail.com +wowway.com +wp.pl +wptamail.com +wrexham.net +writeme.com +writemeback.com +wrongmail.com +www.com +www.e4ward.com +wxs.net +x-mail.net +x-networks.net +x5g.com +xaker.ru +xing886.uu.gl +xmastime.com +xms.nl +xoom.com +xpressmail.zzn.com +xs4all.nl +xsecurity.org +xsmail.com +xtra.co.nz +xuno.com +xww.ro +xy9ce.tk +y7mail.com +ya.ru +yada-yada.com +yahoo.at +yahoo.be +yahoo.ca +yahoo.cn +yahoo.co.id +yahoo.co.il +yahoo.co.in +yahoo.co.jp +yahoo.co.kr +yahoo.co.nz +yahoo.co.th +yahoo.co.uk +yahoo.co.za +yahoo.com +yahoo.com.ar +yahoo.com.au +yahoo.com.br +yahoo.com.cn +yahoo.com.co +yahoo.com.hk +yahoo.com.mx +yahoo.com.my +yahoo.com.ph +yahoo.com.sg +yahoo.com.tr +yahoo.com.tw +yahoo.com.vn +yahoo.cz +yahoo.de +yahoo.dk +yahoo.es +yahoo.fi +yahoo.fr +yahoo.gr +yahoo.hu +yahoo.ie +yahoo.in +yahoo.it +yahoo.jp +yahoo.nl +yahoo.no +yahoo.pl +yahoo.pt +yahoo.ro +yahoo.se +yalla.com +yalla.com.lb +yalook.com +yam.com +yandex.com +yandex.ru +yandex.ua +yapped.net +yawmail.com +yeah.net +yebox.com +yehey.com +yepmail.net +yert.ye.vc +yesey.net +ymail.com +yogotemail.com +yomail.info +yopmail.pp.ua +yopolis.com +yopweb.com +youareadork.com +youmailr.com +your-house.com +your-mail.com +yourname.freeservers.com +yours.com +yoursubdomain.zzn.com +yourteacher.net +yuuhuu.net +yyhmail.com +z1p.biz +za.com +zahadum.com +zaktouni.fr +zeepost.nl +zetmail.com +zhaowei.net +zhouemail.510520.org +ziggo.nl +zionweb.org +zip.net +zipido.com +ziplip.com +zipmail.com +zipmail.com.br +zipmax.com +zmail.ru +zoho.com +zomg.info +zonnet.nl +zoominternet.net +zubee.com +zuzzurello.com +zwallet.com +zweb.in +zxcvbnm.com +zybermail.com +zydecofan.com +zzn.com +zzz.com diff --git a/resources/config/spamfilter/maps/mime_types.map b/resources/config/spamfilter/maps/mime_types.map new file mode 100644 index 00000000..f1eccec5 --- /dev/null +++ b/resources/config/spamfilter/maps/mime_types.map @@ -0,0 +1,129 @@ +bat BAD +chm BAD +com BAD +exe BAD +hta BAD|NZ +iso BAD +jar BAD|NZ +lnk BAD +scr BAD +htm text/html|BAD +html text/html|BAD +shtm text/html|BAD +shtml text/html|BAD +ace BAD|AR +arj BAD|AR +asx BAD +cab BAD|AR +sfx BAD +vst BAD +vss BAD +ade BAD +adp BAD +cmd BAD +cpl BAD +ins BAD +isp BAD +js BAD|NZ +jse BAD +lib BAD +mde BAD +msc BAD +msi BAD +msp BAD +mst BAD +nsh BAD +pif BAD +sct BAD +shb BAD +sys BAD +vb BAD +vbe BAD +vbs BAD|NZ +vxd BAD +wsc BAD +wsh BAD +app BAD +asp BAD +bas BAD +cnt BAD +csh BAD +diagcab BAD +fxp BAD +gadget BAD +grp BAD +hlp BAD +hpj BAD +inf BAD +its BAD +jnlp BAD +ksh BAD +mad BAD +maf BAD +mag BAD +mam BAD +maq BAD +mar BAD +mas BAD +mat BAD +mau BAD +mav BAD +maw BAD +mcf BAD +mda BAD +mdb BAD +mdt BAD +mdw BAD +mdz BAD +msh BAD +msh1 BAD +msh2 BAD +mshxml BAD +msh1xml BAD +msh2xml BAD +msu BAD +ops BAD +osd BAD +pcd BAD +pl BAD +plg BAD +prf BAD +prg BAD +printerexport BAD +ps1 BAD +ps1xml BAD +ps2 BAD +ps2xml BAD +psc1 BAD +psc2 BAD +psd1 BAD +psdm1 BAD +pst BAD +reg BAD +scf BAD +shs BAD +theme BAD +url BAD +vbp BAD +vsmacros BAD +vsw BAD +webpnp BAD +website BAD +ws BAD +xbap BAD +xll BAD +xnk BAD +docx NZ +pdf application/pdf|application/x-pdf|NZ +pptx NZ +wsf NZ +xlsx NZ +7x AR +alz AR +bz2 AR +egg AR +lz AR +rar AR +xz AR +zip AR +txt text/plain|message/disposition-notification|text/rfc822-headers diff --git a/resources/config/sieve/scores.sieve b/resources/config/spamfilter/maps/scores.map similarity index 100% rename from resources/config/sieve/scores.sieve rename to resources/config/spamfilter/maps/scores.map diff --git a/resources/config/spamfilter/maps/spam_trap.list b/resources/config/spamfilter/maps/spam_trap.list new file mode 100644 index 00000000..f51e0275 --- /dev/null +++ b/resources/config/spamfilter/maps/spam_trap.list @@ -0,0 +1,2 @@ +# Spam Trap list +# Messages to addresses listed here are classified as SPAM and rejected by the server. diff --git a/resources/config/spamfilter/maps/suffix_list.dat.gz b/resources/config/spamfilter/maps/suffix_list.dat.gz new file mode 100644 index 0000000000000000000000000000000000000000..c916d9decdd7302b5d41ea210b67dfe6abbe8eff GIT binary patch literal 80969 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 Q_ct-FS07T55qqG`{ibY2NC=eLL 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≥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 AUTOLEARN_SPAM_THRESHOLD || score <= AUTO bayes_train('spamdb/token-insert', body_and_subject, is_spam)"; } -add balance - -# Iterate over tags -let "tags" "var_names()"; -let "i" "count(tags)"; -let "spam_result" ""; -while "i > 0" { - let "i" "i - 1"; - let "tag" "tags[i]"; - let "tag_score" "map('spam/scores', tag)"; - - if eval "is_number(tag_score)" { - let "score" "score + tag_score"; - if eval "ADD_HEADER_SPAM_RESULT" { - if eval "!is_empty(spam_result)" { - let "spam_result" "spam_result + ',\r\n\t' + tag + ' (' + tag_score + ')'"; - } else { - let "spam_result" "spam_result + tag + ' (' + tag_score + ')'"; - } - } - } elsif eval "tag_score == 'reject' { - let "SCORE_REJECT_THRESHOLD" "1"; - let "score" "2"; - break; - } else if eval "tag_score == 'discard'" { - discard; - stop; - } -} - # Process score actions if "SCORE_REJECT_THRESHOLD && score >= SCORE_REJECT_THRESHOLD" { reject "Your message has been rejected because it has an excessive spam score. If you feel this is an error, please contact the postmaster."; diff --git a/resources/config/sieve/from.sieve b/resources/config/spamfilter/scripts/from.sieve similarity index 100% rename from resources/config/sieve/from.sieve rename to resources/config/spamfilter/scripts/from.sieve diff --git a/resources/config/sieve/greylist.sieve b/resources/config/spamfilter/scripts/greylist.sieve similarity index 100% rename from resources/config/sieve/greylist.sieve rename to resources/config/spamfilter/scripts/greylist.sieve diff --git a/resources/config/sieve/headers.sieve b/resources/config/spamfilter/scripts/headers.sieve similarity index 100% rename from resources/config/sieve/headers.sieve rename to resources/config/spamfilter/scripts/headers.sieve diff --git a/resources/config/sieve/helo.sieve b/resources/config/spamfilter/scripts/helo.sieve similarity index 100% rename from resources/config/sieve/helo.sieve rename to resources/config/spamfilter/scripts/helo.sieve diff --git a/resources/config/sieve/html.sieve b/resources/config/spamfilter/scripts/html.sieve similarity index 100% rename from resources/config/sieve/html.sieve rename to resources/config/spamfilter/scripts/html.sieve diff --git a/resources/config/sieve/ip.sieve b/resources/config/spamfilter/scripts/ip.sieve similarity index 100% rename from resources/config/sieve/ip.sieve rename to resources/config/spamfilter/scripts/ip.sieve diff --git a/resources/config/sieve/messageid.sieve b/resources/config/spamfilter/scripts/messageid.sieve similarity index 100% rename from resources/config/sieve/messageid.sieve rename to resources/config/spamfilter/scripts/messageid.sieve diff --git a/resources/config/sieve/mime.sieve b/resources/config/spamfilter/scripts/mime.sieve similarity index 100% rename from resources/config/sieve/mime.sieve rename to resources/config/spamfilter/scripts/mime.sieve diff --git a/resources/config/sieve/prelude.sieve b/resources/config/spamfilter/scripts/prelude.sieve similarity index 100% rename from resources/config/sieve/prelude.sieve rename to resources/config/spamfilter/scripts/prelude.sieve diff --git a/resources/config/sieve/pyzor.sieve b/resources/config/spamfilter/scripts/pyzor.sieve similarity index 100% rename from resources/config/sieve/pyzor.sieve rename to resources/config/spamfilter/scripts/pyzor.sieve diff --git a/resources/config/sieve/rbl.sieve b/resources/config/spamfilter/scripts/rbl.sieve similarity index 96% rename from resources/config/sieve/rbl.sieve rename to resources/config/spamfilter/scripts/rbl.sieve index 19edeee5..fb330538 100644 --- a/resources/config/sieve/rbl.sieve +++ b/resources/config/spamfilter/scripts/rbl.sieve @@ -172,7 +172,10 @@ while "i < domains_len" { let "i" "i + 1"; # Skip invalid and local domain names - if eval "!contains(domain, '.') || is_ip_addr(domain) || is_local_domain(DOMAIN_DIRECTORY, domain_part(domain, 'sld'))" { + if eval "!contains(domain, '.') || + is_ip_addr(domain) || + is_local_domain(DOMAIN_DIRECTORY, domain_part(domain, 'sld')) || + lookup('spam/domains-allow', domain)" { continue; } @@ -321,8 +324,10 @@ while "i < urls_len" { break; } - # Skip URLs pointing to local domains - if eval "is_local_domain(DOMAIN_DIRECTORY, domain_part(uri_part(url, 'host'), 'sld'))" { + # Skip URLs pointing to local or trusted domains + let "domain" "domain_part(uri_part(url, 'host'), 'sld')"; + if eval "is_local_domain(DOMAIN_DIRECTORY, domain) || + lookup('spam/domains-allow', domain)" { continue; } diff --git a/resources/config/sieve/received.sieve b/resources/config/spamfilter/scripts/received.sieve similarity index 100% rename from resources/config/sieve/received.sieve rename to resources/config/spamfilter/scripts/received.sieve diff --git a/resources/config/sieve/recipient.sieve b/resources/config/spamfilter/scripts/recipient.sieve similarity index 100% rename from resources/config/sieve/recipient.sieve rename to resources/config/spamfilter/scripts/recipient.sieve diff --git a/resources/config/sieve/replies_in.sieve b/resources/config/spamfilter/scripts/replies_in.sieve similarity index 100% rename from resources/config/sieve/replies_in.sieve rename to resources/config/spamfilter/scripts/replies_in.sieve diff --git a/resources/config/sieve/replies_out.sieve b/resources/config/spamfilter/scripts/replies_out.sieve similarity index 100% rename from resources/config/sieve/replies_out.sieve rename to resources/config/spamfilter/scripts/replies_out.sieve diff --git a/resources/config/sieve/replyto.sieve b/resources/config/spamfilter/scripts/replyto.sieve similarity index 100% rename from resources/config/sieve/replyto.sieve rename to resources/config/spamfilter/scripts/replyto.sieve diff --git a/resources/config/sieve/reputation.sieve b/resources/config/spamfilter/scripts/reputation.sieve similarity index 100% rename from resources/config/sieve/reputation.sieve rename to resources/config/spamfilter/scripts/reputation.sieve diff --git a/resources/config/spamfilter/scripts/scores.sieve b/resources/config/spamfilter/scripts/scores.sieve new file mode 100644 index 00000000..745adfc2 --- /dev/null +++ b/resources/config/spamfilter/scripts/scores.sieve @@ -0,0 +1,27 @@ +# Add scores +let "tags" "var_names()"; +let "i" "count(tags)"; +let "spam_result" ""; +while "i > 0" { + let "i" "i - 1"; + let "tag" "tags[i]"; + let "tag_score" "map('spam/scores', tag)"; + + if eval "is_number(tag_score)" { + let "score" "score + tag_score"; + if eval "ADD_HEADER_SPAM_RESULT" { + if eval "!is_empty(spam_result)" { + let "spam_result" "spam_result + ',\r\n\t' + tag + ' (' + tag_score + ')'"; + } else { + let "spam_result" "spam_result + tag + ' (' + tag_score + ')'"; + } + } + } elsif eval "tag_score == 'reject' { + let "SCORE_REJECT_THRESHOLD" "1"; + let "score" "2"; + break; + } else if eval "tag_score == 'discard'" { + discard; + stop; + } +} diff --git a/resources/config/sieve/spamtrap.sieve b/resources/config/spamfilter/scripts/spamtrap.sieve similarity index 100% rename from resources/config/sieve/spamtrap.sieve rename to resources/config/spamfilter/scripts/spamtrap.sieve diff --git a/resources/config/sieve/subject.sieve b/resources/config/spamfilter/scripts/subject.sieve similarity index 100% rename from resources/config/sieve/subject.sieve rename to resources/config/spamfilter/scripts/subject.sieve diff --git a/resources/config/sieve/url.sieve b/resources/config/spamfilter/scripts/url.sieve similarity index 90% rename from resources/config/sieve/url.sieve rename to resources/config/spamfilter/scripts/url.sieve index 33d54fe1..99426fdc 100644 --- a/resources/config/sieve/url.sieve +++ b/resources/config/spamfilter/scripts/url.sieve @@ -26,7 +26,8 @@ while "i > 0" { let "host_lc" "to_lowercase(host)"; let "host_sld" "domain_part(host_lc, 'sld')"; - if eval "is_local_domain(DOMAIN_DIRECTORY, host_sld)" { + # Skip local and trusted domains + if eval "is_local_domain(DOMAIN_DIRECTORY, host_sld) || lookup('spam/domains-allow', host_sld)" { continue; } @@ -108,11 +109,11 @@ while "i > 0" { let "t.URI_HIDDEN_PATH" "1"; } - # Phishing checks - if eval "lookup('spam/phishing-open', url)" { + # Phishing checks (refresh OpenPhish every 12 hours, PhishTank every 6 hours) + if eval "lookup_remote('https://openphish.com/feed.txt', url, [43200, 'list'])" { let "t.PHISHED_OPENPHISH" "1"; } - if eval "lookup('spam/phishing-tank', url)" { + if eval "lookup_remote('http://data.phishtank.com/data/online-valid.csv', url, [21600, 'csv', 1, ',', true])" { let "t.PHISHED_PHISHTANK" "1"; } diff --git a/tests/src/imap/mod.rs b/tests/src/imap/mod.rs index b85bc152..fefa32a7 100644 --- a/tests/src/imap/mod.rs +++ b/tests/src/imap/mod.rs @@ -254,7 +254,7 @@ async fn init_imap_tests(delete_if_exists: bool) -> IMAPTest { let imap: Arc = IMAP::init(&config) .await .failed("Invalid configuration file"); - let shutdown_tx = servers.spawn(|server, shutdown_rx| { + let (shutdown_tx, _) = servers.spawn(|server, shutdown_rx| { match &server.protocol { ServerProtocol::Jmap => { server.spawn(JmapSessionManager::new(jmap.clone()), shutdown_rx) diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index 338ffdbb..8f8e5bd8 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -299,7 +299,7 @@ async fn init_jmap_tests(delete_if_exists: bool) -> JMAPTest { let jmap = JMAP::init(&config, &directory, delivery_rx, smtp.clone()) .await .failed("Invalid configuration file"); - let shutdown_tx = servers.spawn(|server, shutdown_rx| { + let (shutdown_tx, _) = servers.spawn(|server, shutdown_rx| { match &server.protocol { ServerProtocol::Smtp | ServerProtocol::Lmtp => { server.spawn(SmtpSessionManager::new(smtp.clone()), shutdown_rx) diff --git a/tests/src/smtp/inbound/antispam.rs b/tests/src/smtp/inbound/antispam.rs index 1af4d95a..c8a5aba1 100644 --- a/tests/src/smtp/inbound/antispam.rs +++ b/tests/src/smtp/inbound/antispam.rs @@ -60,10 +60,11 @@ token-lookup = "SELECT ws, wh FROM bayes_tokens WHERE h1 = ? AND h2 = ?" id-insert = "INSERT INTO seen_ids (id, ttl) VALUES (?, datetime('now', ? || ' seconds'))" id-lookup = "SELECT 1 FROM seen_ids WHERE id = ? AND ttl > CURRENT_TIMESTAMP" id-cleanup = "DELETE FROM seen_ids WHERE ttl < CURRENT_TIMESTAMP" -reputation-insert = "INSERT INTO reputation (token, score, count, last_hit) VALUES (?, ?, 1, CURRENT_TIMESTAMP) +reputation-insert = "INSERT INTO reputation (token, score, count, ttl) VALUES (?, ?, 1, datetime('now', '30 days')) ON CONFLICT(token) - DO UPDATE SET score = (count + 1) * (excluded.score + 0.98 * score) / (0.98 * count + 1), count = count + 1, last_hit = CURRENT_TIMESTAMP" + DO UPDATE SET score = (count + 1) * (excluded.score + 0.98 * score) / (0.98 * count + 1), count = count + 1, ttl = excluded.ttl" reputation-lookup = "SELECT score, count FROM reputation WHERE token = ?" +reputation-cleanup = "DELETE FROM reputation WHERE ttl < CURRENT_TIMESTAMP" [directory."default"] type = "memory" @@ -100,6 +101,10 @@ type = "glob" comment = '#' values = ["spf-dkim-allow.org"] +[directory."spam".lookup."domains-allow"] +type = "glob" +values = [] + [directory."spam".lookup."mime-types"] type = "map" comment = '#' @@ -110,16 +115,6 @@ values = ["html text/html|BAD", "js BAD|NZ", "hta BAD|NZ"] -[directory."spam".lookup."phishing-open"] -type = "glob" -comment = '#' -values = ["*://phishing-open.org", "*://phishing-open.com"] - -[directory."spam".lookup."phishing-tank"] -type = "glob" -comment = '#' -values = ["*://phishing-tank.com", "*://phishing-tank.org"] - [directory."spam".lookup."trap-address"] type = "glob" comment = '#' @@ -151,7 +146,7 @@ PRIMARY KEY (h1, h2) token STRING NOT NULL PRIMARY KEY, score FLOAT NOT NULL DEFAULT '0', count INT(11) NOT NULL DEFAULT '0', -last_hit DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +ttl DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP )", ]; @@ -214,7 +209,8 @@ async fn antispam() { .to_path_buf() .join("resources") .join("config") - .join("sieve"); + .join("spamfilter") + .join("scripts"); let script_config = fs::read_to_string(base_path.join("config.sieve")).unwrap(); let script_prelude = fs::read_to_string(base_path.join("prelude.sieve")).unwrap(); let mut all_scripts = script_config.clone() + "\n" + script_prelude.as_str(); diff --git a/tests/src/smtp/inbound/dnsrbl.rs b/tests/src/smtp/inbound/dnsrbl.rs deleted file mode 100644 index 66f95bd8..00000000 --- a/tests/src/smtp/inbound/dnsrbl.rs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2023 Stalwart Labs Ltd. - * - * This file is part of Stalwart Mail Server. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * in the LICENSE file at the top-level directory of this distribution. - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * You can be released from the requirements of the AGPLv3 license by - * purchasing a commercial license. Please contact licensing@stalw.art - * for more details. -*/ - -use std::time::{Duration, Instant}; - -use smtp::{ - config::IfBlock, - core::{Session, SMTP}, -}; - -use crate::smtp::{inbound::TestQueueEvent, session::TestSession, TestConfig, TestSMTP}; - -#[tokio::test] -async fn dnsrbl() { - /*tracing::subscriber::set_global_default( - tracing_subscriber::FmtSubscriber::builder() - .with_max_level(tracing::Level::TRACE) - .finish(), - ) - .unwrap();*/ - - let mut core = SMTP::test(); - for entry in [ - "1.0.0.10.zen.spamhaus.org", - "2.0.0.10.b.barracudacentral.org", - "spammer.com.dbl.spamhaus.org", - "spammer.net.dbl.spamhaus.org", - "spammer.org.dbl.spamhaus.org", - ] { - core.resolvers.dns.ipv4_add( - entry, - vec!["127.0.0.2".parse().unwrap()], - Instant::now() + Duration::from_secs(10), - ); - } - core.resolvers.dns.ipv4_add( - "shouldwork.org.dbl.spamhaus.org", - vec!["127.255.255.254".parse().unwrap()], - Instant::now() + Duration::from_secs(10), - ); - core.resolvers.dns.ptr_add( - "10.0.0.3".parse().unwrap(), - vec!["spammer.org.".to_string()], - Instant::now() + Duration::from_secs(10), - ); - - let mut qr = core.init_test_queue("smtp_dnsrbl_test"); - let config = &mut core.mail_auth.dnsbl; - config.ip_lookup = vec![ - "zen.spamhaus.org".to_string(), - "bl.spamcop.net".to_string(), - "b.barracudacentral.org".to_string(), - ]; - config.domain_lookup = vec!["dbl.spamhaus.org".to_string()]; - config.verify = IfBlock::new(u32::MAX); - core.session.config.rcpt.relay = IfBlock::new(true); - - // DNSRBL codes other than 127.0.0.0/8 should not be interpreted as block - let mut session = Session::test(core); - session.data.remote_ip = "10.0.0.4".parse().unwrap(); - session.eval_session_params().await; - session.cmd("EHLO shouldwork.org", "250").await; - - // Reject blocked EHLO domains - session - .cmd( - "EHLO spammer.com", - "554 5.7.1 Service unavailable; Domain 'spammer.com' blocked", - ) - .await; - - // Reject blocked return paths - session.ehlo("foobar.org").await; - session - .mail_from( - "list@spammer.com", - "554 5.7.1 Service unavailable; Domain 'spammer.com' blocked", - ) - .await; - - // Reject blocked From addresses - session - .send_message( - "bill@foobar.org", - &["jane@example.org"], - concat!( - "From: Mr. Spammer \r\n", - "To: jane@example.org\r\n", - "Subject: Adwords is expensive, please let me spam you.\r\n\r\n", - "Buy my spammer product\r\n" - ), - "554 5.7.1 Service unavailable; Domain 'spammer.net' blocked", - ) - .await; - - // Reject blocked IPs - session.data.remote_ip = "10.0.0.1".parse().unwrap(); - session.data.iprev.take(); - session.reset_dnsbl_error(); - session.verify_ip_dnsbl().await; - session.ehlo("foobar.org").await; - session - .mail_from( - "bill@foobar.org", - "554 5.7.1 Service unavailable; IP address 10.0.0.1 blocked", - ) - .await; - - session.data.remote_ip = "10.0.0.2".parse().unwrap(); - session.data.iprev.take(); - session.reset_dnsbl_error(); - session.verify_ip_dnsbl().await; - session.ehlo("foobar.org").await; - session - .mail_from( - "bill@foobar.org", - "554 5.7.1 Service unavailable; IP address 10.0.0.2 blocked", - ) - .await; - - // Reject blocked PTR domains - session.data.remote_ip = "10.0.0.3".parse().unwrap(); - session.data.iprev.take(); - session.reset_dnsbl_error(); - session.verify_ip_dnsbl().await; - session.ehlo("foobar.org").await; - session - .mail_from( - "bill@foobar.org", - "554 5.7.1 Service unavailable; Domain 'spammer.org.' blocked", - ) - .await; - - // Non-blocked IPs should work - session.data.remote_ip = "10.0.0.4".parse().unwrap(); - session.data.iprev.take(); - session.reset_dnsbl_error(); - session.verify_ip_dnsbl().await; - session.ehlo("foobar.org").await; - session - .send_message("bill@foobar.org", &["jane@example.org"], "test:dkim", "250") - .await; - qr.read_event().await.unwrap_message(); -} diff --git a/tests/src/smtp/inbound/mod.rs b/tests/src/smtp/inbound/mod.rs index ca830837..326dfd78 100644 --- a/tests/src/smtp/inbound/mod.rs +++ b/tests/src/smtp/inbound/mod.rs @@ -37,7 +37,6 @@ pub mod auth; pub mod basic; pub mod data; pub mod dmarc; -pub mod dnsrbl; pub mod ehlo; pub mod limits; pub mod mail; diff --git a/tests/src/smtp/lookup/utils.rs b/tests/src/smtp/lookup/utils.rs index 8bdfc7f7..27740f24 100644 --- a/tests/src/smtp/lookup/utils.rs +++ b/tests/src/smtp/lookup/utils.rs @@ -21,10 +21,7 @@ * for more details. */ -use std::{ - net::IpAddr, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use mail_auth::{IpLookupStrategy, MX}; @@ -32,7 +29,6 @@ use ::smtp::{config::IfBlock, core::SMTP, outbound::NextHop}; use mail_parser::DateTime; use smtp::{ config::AggregateFrequency, - inbound::ehlo::ToDnsbl, outbound::{ lookup::ToNextHop, mta_sts::{Mode, MxPattern, Policy}, @@ -149,25 +145,6 @@ fn to_remote_hosts() { assert!(mx.to_remote_hosts("domain", 10).is_none()); } -#[test] -fn ip_to_dnsbl() { - assert_eq!( - "2001:DB8:abc:123::42" - .parse::() - .unwrap() - .to_dnsbl("zen.spamhaus.org"), - "2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.2.1.0.c.b.a.0.8.b.d.0.1.0.0.2.zen.spamhaus.org" - ); - - assert_eq!( - "1.2.3.4" - .parse::() - .unwrap() - .to_dnsbl("zen.spamhaus.org"), - "4.3.2.1.zen.spamhaus.org" - ); -} - #[test] fn parse_policy() { for (policy, expected_policy) in [ diff --git a/tests/src/smtp/mod.rs b/tests/src/smtp/mod.rs index a73d5ddc..863c2bf5 100644 --- a/tests/src/smtp/mod.rs +++ b/tests/src/smtp/mod.rs @@ -40,8 +40,8 @@ use smtp::{ config::{ if_block::ConfigIf, queue::ConfigQueue, scripts::SieveContext, session::ConfigSession, throttle::ConfigThrottle, AggregateReport, ArcAuthConfig, Auth, ConfigContext, Connect, - Data, DkimAuthConfig, DmarcAuthConfig, DnsBlConfig, Dsn, Ehlo, EnvelopeKey, Extensions, - IfBlock, IpRevAuthConfig, Mail, MailAuthConfig, Milter, QueueConfig, QueueOutboundSourceIp, + Data, DkimAuthConfig, DmarcAuthConfig, Dsn, Ehlo, EnvelopeKey, Extensions, IfBlock, + IpRevAuthConfig, Mail, MailAuthConfig, Milter, QueueConfig, QueueOutboundSourceIp, QueueOutboundTimeout, QueueOutboundTls, QueueQuotas, QueueThrottle, Rcpt, Report, ReportAnalysis, ReportConfig, SessionConfig, SessionThrottle, SpfAuthConfig, Throttle, VerifyStrategy, @@ -368,11 +368,6 @@ impl TestConfig for MailAuthConfig { iprev: IpRevAuthConfig { verify: IfBlock::new(VerifyStrategy::Relaxed), }, - dnsbl: DnsBlConfig { - verify: IfBlock::new(0), - ip_lookup: vec![], - domain_lookup: vec![], - }, } } } diff --git a/tests/src/smtp/outbound/mod.rs b/tests/src/smtp/outbound/mod.rs index bebc2cdd..09792794 100644 --- a/tests/src/smtp/outbound/mod.rs +++ b/tests/src/smtp/outbound/mod.rs @@ -84,15 +84,17 @@ pub fn start_test_server(core: Arc, protocols: &[ServerProtocol]) -> watch servers.bind(&config); let smtp_manager = SmtpSessionManager::new(core.clone()); let smtp_admin_manager = SmtpAdminSessionManager::new(core); - servers.spawn(|server, shutdown_rx| { - match &server.protocol { - ServerProtocol::Smtp | ServerProtocol::Lmtp => { - server.spawn(smtp_manager.clone(), shutdown_rx) - } - ServerProtocol::Http => server.spawn(smtp_admin_manager.clone(), shutdown_rx), - ServerProtocol::Imap | ServerProtocol::Jmap | ServerProtocol::ManageSieve => { - unreachable!() - } - }; - }) + servers + .spawn(|server, shutdown_rx| { + match &server.protocol { + ServerProtocol::Smtp | ServerProtocol::Lmtp => { + server.spawn(smtp_manager.clone(), shutdown_rx) + } + ServerProtocol::Http => server.spawn(smtp_admin_manager.clone(), shutdown_rx), + ServerProtocol::Imap | ServerProtocol::Jmap | ServerProtocol::ManageSieve => { + unreachable!() + } + }; + }) + .0 }