13 #include "MessageStore.hpp"
14 #include "Session.hpp"
17 #include "is_ascii.hpp"
20 #include <experimental/iterator>
22 #include <fmt/format.h>
23 #include <fmt/ostream.h>
25 #include <boost/algorithm/string/classification.hpp>
26 #include <boost/algorithm/string/split.hpp>
28 #include <boost/xpressive/xpressive.hpp>
32 #include <gflags/gflags.h>
34 using namespace std::string_literals
;
44 <https://www.dnswl.org/?page_id=15#query>
48 The return codes are structured as 127.0.x.y, with “x” indicating the category
49 of an entry and “y” indicating how trustworthy an entry has been judged.
51 Categories (127.0.X.y):
53 2 – Financial services
54 3 – Email Service Providers
55 4 – Organisations (both for-profit [ie companies] and non-profit)
56 5 – Service/network providers
57 6 – Personal/private servers
58 7 – Travel/leisure industry
59 8 – Public sector/governments
60 9 – Media and Tech companies
61 10 – some special cases
62 11 – Education, academic
64 13 – Manufacturing/Industrial
65 14 – Retail/Wholesale/Services
66 15 – Email Marketing Providers
67 20 – Added through Self Service without specific category
69 Trustworthiness / Score (127.0.x.Y):
71 0 = none – only avoid outright blocking (eg large ESP mailservers, -0.1)
72 1 = low – reduce chance of false positives (-1.0)
73 2 = medium – make sure to avoid false positives but allow override for clear
74 cases (-10.0) 3 = high – avoid override (-100.0).
76 The scores in parantheses are typical SpamAssassin scores.
78 Special return code 127.0.0.255
80 In cases where your nameserver issues more than 100’000 queries / 24 hours, you
81 may be blocked from further queries. The return code “127.0.0.255” indicates
87 "b.barracudacentral.org",
88 "sbl-xbl.spamhaus.org",
91 /*** Last octet from A record returned by blocklists ***
93 <https://www.spamhaus.org/faq/section/DNSBL%20Usage#200>
95 Spamhaus uses this general convention for return codes:
97 Return Code Description
98 127.0.0.0/24 Spamhaus IP Blocklists
99 127.0.1.0/24 Spamhaus Domain Blocklists
100 127.0.2.0/24 Spamhaus Zero Reputation Domains list
101 127.255.255.0/24 ERRORS (not implying a "listed" response)
103 Currently used return codes for Spamhaus public IP zones:
105 Return Code Zone Description
106 127.0.0.2 SBL Spamhaus SBL Data
107 127.0.0.3 SBL Spamhaus SBL CSS Data
108 127.0.0.4 XBL CBL Data
109 127.0.0.9 SBL Spamhaus DROP/EDROP Data
110 (in addition to 127.0.0.2, since 01-Jun-2016)
111 127.0.0.10 PBL ISP Maintained
112 127.0.0.11 PBL Spamhaus Maintained
114 127.0.0.5-7 are allocated to XBL for possible future use;
115 127.0.0.8 is allocated to SBL for possible future use.
117 From <https://www.spamhaus.org/faq/section/Spamhaus%20DBL#291>
119 Return Codes Data Source
120 127.0.1.2 spam domain
121 127.0.1.4 phish domain
122 127.0.1.5 malware domain
123 127.0.1.6 botnet C&C domain
124 127.0.1.102 abused legit spam
125 127.0.1.103 abused spammed redirector domain
126 127.0.1.104 abused legit phish
127 127.0.1.105 abused legit malware
128 127.0.1.106 abused legit botnet C&C
129 127.0.1.255 IP queries prohibited!
131 The following special codes indicate an error condition and should not
132 be taken to imply that the queried domain is "listed":
134 Return Code Description
135 127.255.255.252 Typing error in DNSBL name
136 127.255.255.254 Anonymous query through public resolver
137 127.255.255.255 Excessive number of queries
140 From <http://www.surbl.org/lists#multi>
142 last octet indicates which lists it belongs to. The bit positions in
143 that last octet for membership in the different lists are:
153 char const* uribls[]{
159 constexpr auto greeting_wait
= std::chrono::seconds
{6};
160 constexpr int max_recipients_per_message
= 100;
161 constexpr int max_unrecognized_cmds
= 20;
163 // Read timeout value gleaned from RFC-1123 section 5.3.2 and RFC-5321
164 // section 4.5.3.2.7.
165 constexpr auto read_timeout
= std::chrono::minutes
{5};
166 constexpr auto write_timeout
= std::chrono::seconds
{30};
167 } // namespace Config
169 DEFINE_bool(immortal
, false, "don't set process timout");
171 DEFINE_uint64(max_read
, 0, "max data to read");
172 DEFINE_uint64(max_write
, 0, "max data to write");
174 DEFINE_bool(test_mode
, false, "ease up on some checks");
176 DEFINE_bool(use_binarymime
, true, "support BINARYMIME extension, RFC 3030");
177 DEFINE_bool(use_chunking
, true, "support CHUNKING extension, RFC 3030");
178 DEFINE_bool(use_pipelining
, true, "support PIPELINING extension, RFC 2920");
179 DEFINE_bool(use_rrvs
, true, "support RRVS extension, RFC 7293");
180 DEFINE_bool(use_prdr
, true, "support PRDR extension");
181 DEFINE_bool(use_smtputf8
, true, "support SMTPUTF8 extension, RFC 6531");
183 boost::xpressive::mark_tag
secs_(1);
184 boost::xpressive::sregex
const all_rex
= boost::xpressive::icase("wait-all-") >>
185 (secs_
= +boost::xpressive::_d
);
187 Session::Session(fs::path config_path
,
188 std::function
<void(void)> read_hook
,
191 : config_path_(config_path
)
193 , sock_(fd_in
, fd_out
, read_hook
, Config::read_timeout
, Config::write_timeout
)
194 //, send_(config_path, "smtp")
195 //, srs_(config_path)
197 auto accept_db_name
= config_path_
/ "accept_domains";
198 auto allow_db_name
= config_path_
/ "allow";
199 auto block_db_name
= config_path_
/ "block";
200 // auto forward_db_name = config_path_ / "forward";
202 accept_domains_
.open(accept_db_name
);
203 allow_
.open(allow_db_name
);
204 block_
.open(block_db_name
);
205 // forward_.open(forward_db_name);
207 if (sock_
.has_peername() && !IP::is_private(sock_
.us_c_str())) {
208 auto fcrdns
= DNS::fcrdns(res_
, sock_
.us_c_str());
209 for (auto const& fcr
: fcrdns
) {
210 server_fcrdns_
.emplace_back(fcr
);
214 server_identity_
= [this] {
215 auto const id_from_env
{getenv("GHSMTP_SERVER_ID")};
217 return std::string
{id_from_env
};
219 auto const hostname
{osutil::get_hostname()};
220 if (hostname
.find('.') != std::string::npos
)
223 if (!server_fcrdns_
.empty()) {
224 // first result should be shortest
225 return server_fcrdns_
.front().ascii();
228 auto const us_c_str
= sock_
.us_c_str();
229 if (us_c_str
&& !IP::is_private(us_c_str
)) {
230 return IP::to_address_literal(us_c_str
);
233 LOG(FATAL
) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
237 // send_.set_sender(server_identity_);
239 max_msg_size(Config::max_msg_size_initial
);
242 void Session::max_msg_size(size_t max
)
244 max_msg_size_
= max
; // number to advertise via RFC 1870
246 if (FLAGS_max_read
) {
247 sock_
.set_max_read(FLAGS_max_read
);
250 auto const overhead
= std::max(max
/ 10, size_t(2048));
251 sock_
.set_max_read(max
+ overhead
);
255 void Session::bad_host_(char const* msg
) const
257 if (sock_
.has_peername()) {
258 // On my systems, this pattern triggers a fail2ban rule that
259 // blocks connections from this IP address on port 25 for a few
260 // days. See <https://www.fail2ban.org/> for more info.
261 syslog(LOG_MAIL
| LOG_WARNING
, "bad host [%s] %s", sock_
.them_c_str(), msg
);
263 std::exit(EXIT_SUCCESS
);
266 void Session::reset_()
268 // RSET does not force another EHLO/HELO, the one piece of per
269 // transaction data saved is client_identity_:
271 // client_identity_.clear(); <-- not cleared!
273 reverse_path_
.clear();
274 forward_path_
.clear();
275 spf_received_
.clear();
276 // fwd_path_.clear();
277 // fwd_from_.clear();
278 // rep_info_.clear();
288 max_msg_size(max_msg_size());
290 state_
= xact_step::mail
;
294 // Return codes from connection establishment are 220 or 554, according
295 // to RFC 5321. That's it.
297 void Session::greeting()
299 CHECK(state_
== xact_step::helo
);
301 if (sock_
.has_peername()) {
302 close(2); // if we're a networked program, never send to stderr
304 std::string error_msg
;
305 if (!verify_ip_address_(error_msg
)) {
306 LOG(INFO
) << error_msg
;
307 bad_host_(error_msg
.c_str());
310 /******************************************************************
311 <https://tools.ietf.org/html/rfc5321#section-4.3.1> says:
313 4.3. Sequencing of Commands and Replies
315 4.3.1. Sequencing Overview
317 The communication between the sender and receiver is an alternating
318 dialogue, controlled by the sender. As such, the sender issues a
319 command and the receiver responds with a reply. Unless other
320 arrangements are negotiated through service extensions, the sender
321 MUST wait for this response before sending further commands. One
322 important reply is the connection greeting. Normally, a receiver
323 will send a 220 "Service ready" reply when the connection is
324 completed. The sender SHOULD wait for this greeting message before
325 sending any commands.
329 “…the receiver responds with a reply.”
330 “…the sender MUST wait for this response…”
331 “One important reply is the connection greeting.”
332 “The sender SHOULD wait for this greeting…”
334 So is it MUST or SHOULD? I enforce MUST.
335 *******************************************************************/
337 // Wait a bit of time for pre-greeting traffic.
338 if (!(ip_allowed_
|| fcrdns_allowed_
)) {
339 if (sock_
.input_ready(Config::greeting_wait
)) {
340 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush
;
341 LOG(INFO
) << "input before any greeting from " << client_
;
342 bad_host_("input before any greeting");
344 // Give a half greeting and wait again.
345 out_() << "220-" << server_id_() << " ESMTP slowstart - ghsmtp\r\n"
347 if (sock_
.input_ready(Config::greeting_wait
)) {
348 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush
;
349 LOG(INFO
) << "input before full greeting from " << client_
;
350 bad_host_("input before full greeting");
353 <https://www.rfc-editor.org/rfc/rfc5321#section-4.2>
355 An SMTP client MUST determine its actions only by the reply code, not
356 by the text (except for the "change of address" 251 and 551 and, if
357 necessary, 220, 221, and 421 replies); in the general case, any text,
358 including no text at all (although senders SHOULD NOT send bare
359 codes), MUST be acceptable. The space (blank) following the reply
360 code is considered part of the text. Whenever possible, a receiver-
361 SMTP SHOULD test the first digit (severity indication) of the reply
364 Except the following chokes a lot of senders:
366 out_() << "220\r\n" << std::flush;
369 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush
;
372 out_() << "220 " << server_id_() << " ESMTP faststart - ghsmtp\r\n"
377 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush
;
380 LOG(INFO
) << "connect from " << client_
;
382 if ((!FLAGS_immortal
) && (getenv("GHSMTP_IMMORTAL") == nullptr)) {
383 alarm(2 * 60); // initial alarm
387 void Session::flush() { out_() << std::flush
; }
389 void Session::last_in_group_(std::string_view verb
)
391 if (sock_
.input_ready(std::chrono::seconds(0))) {
392 LOG(WARNING
) << "pipelining error; input ready processing " << verb
;
396 void Session::check_for_pipeline_error_(std::string_view verb
)
398 if (!(FLAGS_use_pipelining
&& extensions_
)) {
399 if (sock_
.input_ready(std::chrono::seconds(0))) {
400 LOG(WARNING
) << "pipelining error; input ready processing " << verb
;
405 void Session::lo_(char const* verb
, std::string_view client_identity
)
407 last_in_group_(verb
);
410 if (client_identity_
!= client_identity
) {
411 client_identity_
= client_identity
;
413 std::string error_msg
;
414 if (!verify_client_(client_identity_
, error_msg
)) {
415 LOG(INFO
) << "client identity blocked: " << error_msg
;
416 bad_host_(error_msg
.c_str());
422 out_() << "250 " << server_id_() << "\r\n";
428 if (sock_
.has_peername()) {
429 out_() << "250-" << server_id_() << " at your service, " << client_
433 out_() << "250-" << server_id_() << "\r\n";
436 // LIMITS SMTP Service Extension,
437 // <https://www.rfc-editor.org/rfc/rfc9422.html>
438 out_() << "250-LIMITS RCPTMAX=" << Config::max_recipients_per_message
440 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
441 out_() << "250-8BITMIME\r\n"; // RFC 6152
443 if (FLAGS_use_rrvs
) {
444 out_() << "250-RRVS\r\n"; // RFC 7293
447 if (FLAGS_use_prdr
) {
448 out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
452 // Check sasl sources for auth types.
453 // out_() << "250-AUTH PLAIN\r\n";
454 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
457 // If we're not already TLS, offer TLS
458 out_() << "250-STARTTLS\r\n"; // RFC 3207
461 out_() << "250-ENHANCEDSTATUSCODES\r\n"; // RFC 2034
463 if (FLAGS_use_pipelining
) {
464 out_() << "250-PIPELINING\r\n"; // RFC 2920
467 if (FLAGS_use_binarymime
) {
468 out_() << "250-BINARYMIME\r\n"; // RFC 3030
471 if (FLAGS_use_chunking
) {
472 out_() << "250-CHUNKING\r\n"; // RFC 3030
475 if (FLAGS_use_smtputf8
) {
476 out_() << "250-SMTPUTF8\r\n"; // RFC 6531
479 out_() << "250 HELP\r\n";
482 out_() << std::flush
;
484 if (sock_
.has_peername()) {
485 if (std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
486 client_identity_
) != end(client_fcrdns_
)) {
487 LOG(INFO
) << verb
<< " " << client_identity
<< " from "
488 << sock_
.them_address_literal();
491 LOG(INFO
) << verb
<< " " << client_identity
<< " from " << client_
;
495 LOG(INFO
) << verb
<< " " << client_identity
;
499 void Session::mail_from(Mailbox
&& reverse_path
, parameters_t
const& parameters
)
501 check_for_pipeline_error_("MAIL FROM");
504 case xact_step::helo
:
505 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
506 LOG(WARNING
) << "'MAIL FROM' before HELO/EHLO"
507 << (sock_
.has_peername() ? " from " : "") << client_
;
509 case xact_step::mail
: break;
510 case xact_step::rcpt
:
511 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
512 LOG(WARNING
) << "nested MAIL command"
513 << (sock_
.has_peername() ? " from " : "") << client_
;
515 case xact_step::data
:
516 case xact_step::bdat
:
517 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush
;
518 LOG(WARNING
) << "nested MAIL command"
519 << (sock_
.has_peername() ? " from " : "") << client_
;
521 case xact_step::rset
:
522 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
523 LOG(WARNING
) << "error state must be cleared with a RSET"
524 << (sock_
.has_peername() ? " from " : "") << client_
;
528 if (!verify_from_params_(parameters
)) {
532 if (!smtputf8_
&& !is_ascii(reverse_path
.local_part())) {
533 LOG(WARNING
) << "non ascii reverse_path \"" << reverse_path
534 << "\" without SMTPUTF8 paramater";
537 std::string error_msg
;
538 if (!verify_sender_(reverse_path
, error_msg
)) {
539 LOG(INFO
) << "verify sender failed: " << error_msg
;
540 bad_host_(error_msg
.c_str());
543 reverse_path_
= std::move(reverse_path
);
544 // fwd_path_.clear();
545 // fwd_from_.clear();
546 forward_path_
.clear();
547 out_() << "250 2.1.0 MAIL FROM OK\r\n";
548 // No flush RFC-2920 section 3.1, this could be part of a command group.
550 fmt::memory_buffer params
;
551 for (auto const& [name
, value
] : parameters
) {
552 fmt::format_to(std::back_inserter(params
), " {}", name
);
553 if (!value
.empty()) {
554 fmt::format_to(std::back_inserter(params
), "={}", value
);
557 LOG(INFO
) << "MAIL FROM:<" << reverse_path_
<< ">" << fmt::to_string(params
);
559 state_
= xact_step::rcpt
;
562 void Session::rcpt_to(Mailbox
&& forward_path
, parameters_t
const& parameters
)
564 check_for_pipeline_error_("RCPT TO");
567 case xact_step::helo
:
568 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
569 LOG(WARNING
) << "'RCPT TO' before HELO/EHLO"
570 << (sock_
.has_peername() ? " from " : "") << client_
;
572 case xact_step::mail
:
573 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
574 LOG(WARNING
) << "'RCPT TO' before 'MAIL FROM'"
575 << (sock_
.has_peername() ? " from " : "") << client_
;
577 case xact_step::rcpt
:
578 case xact_step::data
: break;
579 case xact_step::bdat
:
580 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
581 LOG(WARNING
) << "'RCPT TO' during BDAT transfer"
582 << (sock_
.has_peername() ? " from " : "") << client_
;
584 case xact_step::rset
:
585 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
586 LOG(WARNING
) << "error state must be cleared with a RSET"
587 << (sock_
.has_peername() ? " from " : "") << client_
;
591 if (!verify_rcpt_params_(parameters
))
594 if (!verify_recipient_(forward_path
))
597 if (!smtputf8_
&& !is_ascii(forward_path
.local_part())) {
598 LOG(WARNING
) << "non ascii forward_path \"" << forward_path
599 << "\" without SMTPUTF8 paramater";
602 if (forward_path_
.size() >= Config::max_recipients_per_message
) {
603 out_() << "452 4.5.3 too many recipients\r\n" << std::flush
;
604 LOG(WARNING
) << "too many recipients <" << forward_path
<< ">";
607 // no check for dups, postfix doesn't
608 forward_path_
.emplace_back(std::move(forward_path
));
610 Mailbox
const& rcpt_to_mbx
= forward_path_
.back();
612 LOG(INFO
) << "RCPT TO:<" << rcpt_to_mbx
<< ">";
614 // No flush RFC-2920 section 3.1, this could be part of a command group.
615 out_() << "250 2.1.5 RCPT TO OK\r\n";
617 state_
= xact_step::data
;
620 // The headers Return-Path:, Received-SPF:, and Received: are returned
623 std::string
Session::added_headers_(MessageStore
const& msg
)
625 auto const protocol
{[this]() {
626 if (sock_
.tls() && !extensions_
) {
627 LOG(WARNING
) << "TLS active without extensions";
629 // <https://www.iana.org/assignments/mail-parameters/mail-parameters.xhtml#mail-parameters-5>
631 return sock_
.tls() ? "UTF8SMTPS" : "UTF8SMTP";
632 else if (sock_
.tls())
634 else if (extensions_
)
640 fmt::memory_buffer headers
;
643 fmt::format_to(std::back_inserter(headers
), "Return-Path: <{}>\r\n",
644 reverse_path_
.as_string());
647 if (!spf_received_
.empty()) {
648 fmt::format_to(std::back_inserter(headers
), "{}\r\n", spf_received_
);
652 // <https://tools.ietf.org/html/rfc5321#section-4.4>
653 fmt::format_to(std::back_inserter(headers
), "Received: from {}",
654 client_identity_
.utf8());
655 if (sock_
.has_peername()) {
656 fmt::format_to(std::back_inserter(headers
), " ({})", client_
);
658 fmt::format_to(std::back_inserter(headers
), "\r\n\tby {} with {} id {}",
659 server_identity_
.utf8(), protocol
, msg
.id().as_string_view());
660 if (forward_path_
.size()) {
661 fmt::format_to(std::back_inserter(headers
), "\r\n\tfor <{}>",
662 forward_path_
[0].as_string());
663 // From <https://datatracker.ietf.org/doc/html/rfc5321#section-4.4>:
664 // “If the FOR clause appears, it MUST contain exactly one <path>
665 // entry, even when multiple RCPT commands have been given. Multiple
666 // <path>s raise some security issues and have been deprecated, see
668 // for (auto i = 1u; i < forward_path_.size(); ++i)
669 // fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
671 std::string
const tls_info
{sock_
.tls_info()};
672 if (tls_info
.length()) {
673 fmt::format_to(std::back_inserter(headers
), "\r\n\t({})", tls_info
);
675 fmt::format_to(std::back_inserter(headers
), ";\r\n\t{}\r\n",
676 msg
.when().as_string_view());
678 return fmt::to_string(headers
);
682 bool lookup_domain(CDB
& cdb
, Domain
const& domain
)
684 if (!domain
.empty()) {
685 if (cdb
.contains(domain
.ascii())) {
688 if (domain
.is_unicode() && cdb
.contains(domain
.utf8())) {
696 std::tuple
<Session::SpamStatus
, std::string
> Session::spam_status_()
698 if (spf_result_
== SPF::Result::FAIL
&& !ip_allowed_
)
699 return {SpamStatus::spam
, "SPF failed"};
701 // These should have already been rejected by verify_client_().
702 if ((reverse_path_
.domain() == "localhost.local") ||
703 (reverse_path_
.domain() == "localhost"))
704 return {SpamStatus::spam
, "bogus reverse_path"};
706 std::vector
<std::string
> why_ham
;
708 // Anything enciphered tastes a lot like ham.
710 why_ham
.emplace_back("they used TLS");
712 if (spf_result_
== SPF::Result::PASS
) {
713 if (lookup_domain(allow_
, spf_sender_domain_
)) {
714 why_ham
.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
715 spf_sender_domain_
.utf8()));
718 auto tld_dom
{tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
719 if (tld_dom
&& allow_
.contains(tld_dom
)) {
720 why_ham
.emplace_back(fmt::format(
721 "SPF sender registered domain ({}) is allowed", tld_dom
));
727 why_ham
.emplace_back(
728 fmt::format("FCrDNS (or it's registered domain) is allowed"));
730 if (!why_ham
.empty())
731 return {SpamStatus::ham
,
732 fmt::format("{}", fmt::join(std::begin(why_ham
), std::end(why_ham
),
735 return {SpamStatus::spam
, "it's not ham"};
738 static std::string
folder(Session::SpamStatus status
,
739 std::vector
<Mailbox
> const& forward_path
,
740 Mailbox
const& reverse_path
)
743 Mailbox("gene.hightower+caf_=forwarded-gmail=digilicious.com@gmail.com"))
746 if (reverse_path
== Mailbox("ietf-smtp-bounces@ietf.org"))
750 std::string_view local_part
;
751 std::string_view folder
;
754 assignment assignments
[] = {
755 {"bootstrappable", ".bootstrappable"},
756 {"coreboot.org", ".coreboot"},
758 {"dns-privacy", ".dns-privacy"},
759 {"dnsmasq", ".INBOX.DNSmasq"},
760 {"emailcore", ".emailcore"},
761 {"fucking-facebook", ".FB"},
762 {"gene-ebay", ".EBay"},
763 {"i-hate-facebook", ".FB"},
764 {"i-hate-linked-in", ".linkedin"},
765 {"mailop", ".INBOX.mailop"},
766 {"modelfkeyboards.com", ""},
767 {"nest", ".INBOX.Nest"},
768 {"opendmarc-dev", ".dmarc"},
769 {"opendmarc-users", ".dmarc"},
770 {"papasys.com", ".INBOX.PAPA"},
771 {"postmaster-rua", ".INBOX.rua"},
772 {"quic=ietf.org", ".INBOX.quic"},
773 {"shadowserver-reports@digilicious.com", ".INBOX.shadowserver"},
774 {"theatlantic.com", ""},
775 {"time-nutz", ".time-nutz"},
776 {"zfsonlinux.topicbox.com", ".INBOX.zfs"},
779 for (auto ass
: assignments
) {
780 if (iequal(forward_path
[0].local_part(), ass
.local_part
))
781 return std::string(ass
.folder
);
784 if (iends_with(forward_path
[0].local_part(), "-at-duck"))
787 if (status
== Session::SpamStatus::spam
)
793 bool Session::msg_new()
795 CHECK((state_
== xact_step::data
) || (state_
== xact_step::bdat
));
797 auto const& [status
, reason
]{spam_status_()};
799 LOG(INFO
) << ((status
== SpamStatus::ham
) ? "ham since " : "spam since ")
802 // All sources of ham get a fresh 5 minute timeout per message.
803 if (status
== SpamStatus::ham
) {
804 if ((!FLAGS_immortal
) && (getenv("GHSMTP_IMMORTAL") == nullptr))
808 msg_
= std::make_unique
<MessageStore
>();
810 if (!FLAGS_max_write
)
811 FLAGS_max_write
= max_msg_size();
814 msg_
->open(server_id_(), FLAGS_max_write
,
815 folder(status
, forward_path_
, reverse_path_
));
816 auto const hdrs
{added_headers_(*(msg_
.get()))};
819 // fmt::memory_buffer spam_status;
820 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
821 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
822 // msg_->write(spam_status.data(), spam_status.size());
824 LOG(INFO
) << "Spam-Status: "
825 << ((status
== SpamStatus::spam
) ? "Yes" : "No") << ", "
830 catch (std::system_error
const& e
) {
833 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
834 LOG(ERROR
) << "no space";
840 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
841 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
842 LOG(ERROR
) << e
.what();
848 catch (std::exception
const& e
) {
849 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
850 LOG(ERROR
) << e
.what();
856 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
857 LOG(ERROR
) << "msg_new failed with no exception caught";
863 bool Session::msg_write(char const* s
, std::streamsize count
)
865 if ((state_
!= xact_step::data
) && (state_
!= xact_step::bdat
))
872 if (msg_
->write(s
, count
))
875 catch (std::system_error
const& e
) {
878 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
879 LOG(ERROR
) << "no space";
885 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
886 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
887 LOG(ERROR
) << e
.what();
893 catch (std::exception
const& e
) {
894 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
895 LOG(ERROR
) << e
.what();
901 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
902 LOG(ERROR
) << "msg_write failed with no exception caught";
908 bool Session::data_start()
910 last_in_group_("DATA");
913 case xact_step::helo
:
914 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
915 LOG(WARNING
) << "'DATA' before HELO/EHLO"
916 << (sock_
.has_peername() ? " from " : "") << client_
;
918 case xact_step::mail
:
919 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
920 LOG(WARNING
) << "'DATA' before 'MAIL FROM'"
921 << (sock_
.has_peername() ? " from " : "") << client_
;
923 case xact_step::rcpt
:
925 /******************************************************************
926 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
928 The DATA command can fail for only two reasons:
930 If there was no MAIL, or no RCPT, command, or all such commands were
931 rejected, the server MAY return a "command out of sequence" (503) or
932 "no valid recipients" (554) reply in response to the DATA command.
934 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
936 The additional restriction is that when there have been no successful
937 RCPT commands in the mail transaction, the DATA command MUST fail
938 with a 503 reply code.
940 Therefore I will send the reply code that is valid for both, and
941 do the same for the BDAT case.
942 *******************************************************************/
944 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
945 LOG(WARNING
) << "no valid recipients"
946 << (sock_
.has_peername() ? " from " : "") << client_
;
948 case xact_step::data
: break;
949 case xact_step::bdat
:
950 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
951 LOG(WARNING
) << "'DATA' during BDAT transfer"
952 << (sock_
.has_peername() ? " from " : "") << client_
;
954 case xact_step::rset
:
955 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
956 LOG(WARNING
) << "error state must be cleared with a RSET"
957 << (sock_
.has_peername() ? " from " : "") << client_
;
962 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
964 LOG(WARNING
) << "DATA does not support BINARYMIME";
965 state_
= xact_step::rset
; // RFC 3030 section 3 page 5
970 LOG(ERROR
) << "msg_new() failed";
974 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush
;
979 bool Session::do_deliver_()
987 catch (std::system_error
const& e
) {
990 out_() << "452 4.3.1 mail system full\r\n" << std::flush
;
991 LOG(ERROR
) << "no space";
997 out_() << "451 4.3.0 mail system error\r\n" << std::flush
;
999 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
1000 LOG(ERROR
) << e
.what();
1007 LOG(INFO
) << "message delivered, " << msg_
->size() << " octets, with id "
1012 void Session::xfer_response_(std::string_view success_msg
)
1014 auto bad_recipients_db_name
= config_path_
/ "bad_recipients_data";
1015 CDB bad_recipients_db
;
1016 if (!bad_recipients_db
.open(bad_recipients_db_name
)) {
1017 LOG(WARNING
) << "can't open bad_recipients_data";
1020 auto temp_fail_db_name
= config_path_
/ "temp_fail_data";
1022 if (!temp_fail_db
.open(temp_fail_db_name
)) {
1023 LOG(WARNING
) << "can't open temp_fail_data";
1026 std::vector
<std::string
> bad_recipients
;
1027 if (bad_recipients_db
.is_open()) {
1028 for (auto fp
: forward_path_
) {
1029 if (bad_recipients_db
.contains_lc(fp
.local_part())) {
1030 bad_recipients
.push_back(fp
);
1031 LOG(WARNING
) << "bad recipient " << fp
;
1035 std::vector
<std::string
> temp_failed
;
1036 if (temp_fail_db
.is_open()) {
1037 for (auto fp
: forward_path_
) {
1038 if (temp_fail_db
.contains_lc(fp
.local_part())) {
1039 temp_failed
.push_back(fp
);
1040 LOG(WARNING
) << "temp failed recipient " << fp
;
1045 if (prdr_
&& forward_path_
.size() > 1 &&
1046 (bad_recipients
.size() || temp_failed
.size())) {
1048 if (forward_path_
.size() == bad_recipients
.size()) {
1049 out_() << "550 5.1.1 all recipients bad\r\n";
1051 else if (forward_path_
.size() == temp_failed
.size()) {
1052 out_() << "450 4.1.1 temporary failure for all recipients\r\n";
1055 // this is the mixed situation
1056 out_() << "353 per recipient responses follow:\r\n";
1057 for (auto fp
: forward_path_
) {
1058 if (bad_recipients_db
.is_open() && bad_recipients_db
.contains_lc(fp
.local_part())) {
1059 out_() << "550 5.1.1 bad recipient " << fp
<< "\r\n";
1060 LOG(INFO
) << "bad recipient " << fp
;
1062 else if (temp_fail_db
.is_open() && temp_fail_db
.contains_lc(fp
.local_part())) {
1063 out_() << "450 4.1.1 temporary failure for " << fp
<< "\r\n";
1064 LOG(INFO
) << "temp fail for " << fp
;
1067 out_() << "250 2.0.0 success for " << fp
<< "\r\n";
1068 LOG(INFO
) << "success for " << fp
;
1072 // after the per recipient status, a final and I think useless message.
1073 if (forward_path_
.size() > (bad_recipients
.size() + temp_failed
.size())) {
1074 out_() << "250 2.0.0 success for some recipients\r\n";
1076 else if (temp_failed
.size()) {
1077 out_() << "450 4.1.1 temporary failure for some recipients\r\n";
1080 out_() << "550 5.1.1 some bad recipients\r\n";
1085 if (bad_recipients
.size()) {
1086 out_() << "550 5.1.1 bad recipient(s) ";
1087 std::copy(begin(bad_recipients
), end(bad_recipients
),
1088 std::experimental::make_ostream_joiner(out_(), ", "));
1091 else if (temp_failed
.size()) {
1092 out_() << "450 4.1.1 temporary failure for ";
1093 std::copy(begin(temp_failed
), end(temp_failed
),
1094 std::experimental::make_ostream_joiner(out_(), ", "));
1098 out_() << "250 2.0.0 " << success_msg
<< " OK\r\n";
1103 void Session::data_done()
1105 CHECK((state_
== xact_step::data
));
1107 if (msg_
&& msg_
->size_error()) {
1112 // Check for and act on magic "wait" address.
1114 using namespace boost::xpressive
;
1116 sregex
const rex
= icase("wait-data-") >> (secs_
= +_d
);
1119 for (auto fp
: forward_path_
) {
1120 if (regex_match(fp
.local_part(), what
, rex
) ||
1121 regex_match(fp
.local_part(), what
, all_rex
)) {
1122 auto const str
= what
[secs_
].str();
1123 LOG(INFO
) << "waiting at DATA " << str
<< " seconds";
1125 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1126 google::FlushLogFiles(google::INFO
);
1127 out_() << std::flush
;
1129 LOG(INFO
) << "done waiting";
1134 if (!do_deliver_()) {
1138 xfer_response_("DATA");
1140 out_() << std::flush
;
1144 void Session::data_size_error()
1146 out_().clear(); // clear possible eof from input side
1147 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1151 LOG(WARNING
) << "DATA size error";
1155 void Session::data_error()
1157 out_().clear(); // clear possible eof from input side
1158 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush
;
1162 LOG(WARNING
) << "DATA error";
1166 bool Session::bdat_start(size_t n
)
1168 // In practice, this one gets pipelined.
1169 // last_in_group_("BDAT");
1172 case xact_step::helo
:
1173 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
1174 LOG(WARNING
) << "'BDAT' before HELO/EHLO"
1175 << (sock_
.has_peername() ? " from " : "") << client_
;
1177 case xact_step::mail
:
1178 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
1179 LOG(WARNING
) << "'BDAT' before 'MAIL FROM'"
1180 << (sock_
.has_peername() ? " from " : "") << client_
;
1182 case xact_step::rcpt
:
1183 // See comment in data_start()
1184 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
1185 LOG(WARNING
) << "no valid recipients"
1186 << (sock_
.has_peername() ? " from " : "") << client_
;
1188 case xact_step::data
: // first bdat
1190 case xact_step::bdat
: return true;
1191 case xact_step::rset
:
1192 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
1193 LOG(WARNING
) << "error state must be cleared with a RSET"
1194 << (sock_
.has_peername() ? " from " : "") << client_
;
1198 state_
= xact_step::bdat
;
1203 void Session::bdat_done(size_t n
, bool last
)
1205 if (state_
!= xact_step::bdat
) {
1214 if (msg_
->size_error()) {
1220 out_() << "250 2.0.0 BDAT " << n
<< " OK\r\n" << std::flush
;
1221 LOG(INFO
) << "BDAT " << n
;
1225 LOG(INFO
) << "BDAT " << n
<< " LAST";
1227 // Check for and act on magic "wait" address.
1229 using namespace boost::xpressive
;
1231 sregex
const rex
= icase("wait-bdat-") >> (secs_
= +_d
);
1234 for (auto fp
: forward_path_
) {
1235 if (regex_match(fp
.local_part(), what
, rex
) ||
1236 regex_match(fp
.local_part(), what
, all_rex
)) {
1237 auto const str
= what
[secs_
].str();
1238 LOG(INFO
) << "waiting at BDAT " << str
<< " seconds";
1240 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1241 google::FlushLogFiles(google::INFO
);
1242 out_() << std::flush
;
1244 LOG(INFO
) << "done waiting";
1249 if (!do_deliver_()) {
1253 xfer_response_(fmt::format("BDAT {} LAST", n
));
1255 out_() << std::flush
;
1259 void Session::bdat_size_error()
1261 out_().clear(); // clear possible eof from input side
1262 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1266 LOG(WARNING
) << "BDAT size error";
1270 void Session::bdat_seq_error()
1272 out_().clear(); // clear possible eof from input side
1273 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush
;
1277 LOG(WARNING
) << "BDAT sequence error";
1281 void Session::bdat_io_error()
1283 out_().clear(); // clear possible eof from input side
1284 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush
;
1288 LOG(WARNING
) << "BDAT I/O error";
1292 void Session::rset()
1294 out_() << "250 2.1.5 RSET OK\r\n";
1295 // No flush RFC-2920 section 3.1, this could be part of a command group.
1296 LOG(INFO
) << "RSET";
1300 void Session::noop(std::string_view str
)
1302 last_in_group_("NOOP");
1303 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush
;
1304 LOG(INFO
) << "NOOP" << (str
.length() ? " " : "") << str
;
1307 void Session::vrfy(std::string_view str
)
1309 last_in_group_("VRFY");
1310 out_() << "252 2.1.5 try it\r\n" << std::flush
;
1311 LOG(INFO
) << "VRFY" << (str
.length() ? " " : "") << str
;
1314 void Session::help(std::string_view str
)
1316 if (iequal(str
, "help\r\n")) {
1317 out_() << "214 2.0.0 Now you're sounding desperate.\r\n" << std::flush
;
1320 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n"
1323 LOG(INFO
) << "HELP" << (str
.length() ? " " : "") << str
;
1326 void Session::quit()
1329 // last_in_group_("QUIT");
1330 out_() << "221 2.0.0 closing connection\r\n" << std::flush
;
1331 LOG(INFO
) << "QUIT";
1335 void Session::auth()
1337 out_() << "454 4.7.0 authentication failure\r\n" << std::flush
;
1338 LOG(INFO
) << "AUTH";
1342 void Session::error(std::string_view log_msg
)
1344 out_() << "421 4.3.5 system error: " << log_msg
<< "\r\n" << std::flush
;
1345 LOG(WARNING
) << log_msg
;
1348 void Session::cmd_unrecognized(std::string_view cmd
)
1350 auto const escaped
{esc(cmd
)};
1351 LOG(WARNING
) << "command unrecognized: \"" << escaped
<< "\"";
1353 if (++n_unrecognized_cmds_
>= Config::max_unrecognized_cmds
) {
1354 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1355 << "\" exceeds limit\r\n"
1357 LOG(WARNING
) << n_unrecognized_cmds_
1358 << " unrecognized commands is too many";
1362 out_() << "500 5.5.1 command unrecognized: \"" << escaped
<< "\"\r\n"
1366 void Session::bare_lf()
1368 // Error code used by Office 365.
1369 out_() << "554 5.6.11 bare LF\r\n" << std::flush
;
1370 LOG(WARNING
) << "bare LF";
1374 void Session::max_out()
1376 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1377 LOG(WARNING
) << "message size maxed out";
1381 void Session::time_out()
1383 out_() << "421 4.4.2 time-out\r\n" << std::flush
;
1384 LOG(WARNING
) << "time-out" << (sock_
.has_peername() ? " from " : "")
1389 void Session::starttls()
1391 last_in_group_("STARTTLS");
1393 out_() << "554 5.5.1 TLS already active\r\n" << std::flush
;
1394 LOG(WARNING
) << "STARTTLS issued with TLS already active";
1396 else if (!extensions_
) {
1397 out_() << "554 5.5.1 TLS not avaliable without using EHLO\r\n"
1399 LOG(WARNING
) << "STARTTLS issued without using EHLO";
1402 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush
;
1403 if (sock_
.starttls_server(config_path_
)) {
1405 max_msg_size(Config::max_msg_size_bro
);
1406 LOG(INFO
) << "STARTTLS " << sock_
.tls_info();
1409 LOG(INFO
) << "failed STARTTLS";
1414 void Session::exit_()
1416 // sock_.log_totals();
1418 timespec time_used
{};
1419 clock_gettime(CLOCK_PROCESS_CPUTIME_ID
, &time_used
);
1421 LOG(INFO
) << "CPU time " << time_used
.tv_sec
<< "." << std::setw(9)
1422 << std::setfill('0') << time_used
.tv_nsec
<< " seconds";
1424 std::exit(EXIT_SUCCESS
);
1427 /////////////////////////////////////////////////////////////////////////////
1429 // All of the verify_* functions send their own error messages back to
1430 // the client on failure, and return false.
1432 bool Session::verify_ip_address_(std::string
& error_msg
)
1434 auto ip_block_db_name
= config_path_
/ "ip-block";
1436 if (ip_block
.open(ip_block_db_name
) &&
1437 ip_block
.contains(sock_
.them_c_str())) {
1439 fmt::format("IP address {} on static blocklist", sock_
.them_c_str());
1440 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1444 client_fcrdns_
.clear();
1446 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1447 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1448 LOG(INFO
) << "loopback address allowed";
1450 client_fcrdns_
.emplace_back("localhost");
1451 client_
= fmt::format("localhost {}", sock_
.them_address_literal());
1455 auto const fcrdns
= DNS::fcrdns(res_
, sock_
.them_c_str());
1456 for (auto const& fcr
: fcrdns
) {
1457 client_fcrdns_
.emplace_back(fcr
);
1460 if (IP::is_private(sock_
.them_address_literal())) {
1461 LOG(INFO
) << "private address allowed";
1463 client_
= sock_
.them_address_literal();
1467 if (!client_fcrdns_
.empty()) {
1468 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1469 sock_
.them_address_literal());
1471 for (auto const& client_fcrdns
: client_fcrdns_
) {
1472 if (allow_
.contains_lc(client_fcrdns
.ascii())) {
1473 LOG(INFO
) << "FCrDNS " << client_fcrdns
<< " allowed";
1474 fcrdns_allowed_
= true;
1477 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1479 if (allow_
.contains(tld
)) {
1480 LOG(INFO
) << "FCrDNS registered domain " << tld
<< " allowed";
1481 fcrdns_allowed_
= true;
1487 for (auto const& client_fcrdns
: client_fcrdns_
) {
1488 if (block_
.contains_lc(client_fcrdns
.ascii())) {
1490 fmt::format("FCrDNS {} on static blocklist", client_fcrdns
.ascii());
1491 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1495 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1497 if (block_
.contains(tld
)) {
1498 error_msg
= fmt::format(
1499 "FCrDNS registered domain {} on static blocklist", tld
);
1500 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1507 client_
= fmt::format("{}", sock_
.them_address_literal());
1510 if (IP4::is_address(sock_
.them_c_str())) {
1512 auto const reversed
{IP4::reverse(sock_
.them_c_str())};
1515 // Check with allow list.
1516 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1519 for (auto wl : Config::wls) {
1520 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1521 if (q.has_record()) {
1522 using namespace boost::xpressive;
1524 auto const as = q.get_strings()[0];
1525 LOG(INFO) << "on allow list " << wl << " as " << as;
1529 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1532 if (regex_match(as, what, rex)) {
1533 auto const x = what[x_].str();
1534 auto const y = what[y_].str();
1537 std::from_chars(y.data(), y.data() + y.size(), value);
1540 LOG(INFO) << "allowed";
1544 LOG(INFO) << "Any A record skips check on block list";
1550 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1551 std::shuffle(std::begin(Config::bls
), std::end(Config::bls
),
1554 for (auto bl
: Config::bls
) {
1555 DNS::Query
q(res_
, DNS::RR_type::A
, reversed
+ bl
);
1556 if (q
.has_record()) {
1557 const auto a_strings
= q
.get_strings();
1558 for (auto const& as
: a_strings
) {
1559 LOG(INFO
) << bl
<< " returned " << as
;
1561 for (auto const& as
: a_strings
) {
1562 if (as
== "127.0.0.1") {
1563 LOG(INFO
) << "Should never get 127.0.0.1, from " << bl
;
1565 else if (as
== "127.0.0.10" || as
== "127.0.0.11") {
1566 LOG(INFO
) << "PBL listed, ignoring " << bl
;
1568 else if (as
== "127.255.255.252") {
1569 LOG(INFO
) << "Typing error in DNSBL name " << bl
;
1571 else if (as
== "127.255.255.254") {
1572 LOG(INFO
) << "Anonymous query through public resolver " << bl
;
1574 else if (as
== "127.255.255.255") {
1575 LOG(INFO
) << "Excessive number of queries " << bl
;
1578 error_msg
= fmt::format("IP address {} blocked: {} returned {}",
1579 sock_
.them_c_str(), bl
, as
);
1580 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1586 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1589 LOG(INFO
) << "IP address okay";
1593 bool domain_blocked(DNS::Resolver
& res
, Domain
const& identity
)
1595 if (identity
.is_address_literal()) {
1596 // don't "domain block" address literals
1599 if (!identity
.ascii().empty()) {
1600 Domain lookup
{fmt::format("{}.dbl.spamhaus.org", identity
.ascii())};
1601 DNS::Query
q(res
, DNS::RR_type::A
, lookup
.ascii());
1602 if (q
.has_record()) {
1603 const auto a_strings
= q
.get_strings();
1604 for (auto const& as
: a_strings
) {
1605 if (istarts_with(as
, "127.0.1.")) {
1606 LOG(INFO
) << "Domain " << identity
<< " blocked by spamhaus, " << as
;
1615 // check the identity from HELO/EHLO
1616 bool Session::verify_client_(Domain
const& client_identity
,
1617 std::string
& error_msg
)
1619 if (!client_fcrdns_
.empty()) {
1620 if (auto id
= std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
1622 id
!= end(client_fcrdns_
)) {
1623 // If the HELO ident is one of the FCrDNS names...
1624 if (id
!= begin(client_fcrdns_
)) {
1625 // ...then rotate that one to the front of the list
1626 std::rotate(begin(client_fcrdns_
), id
, id
+ 1);
1628 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1629 sock_
.them_address_literal());
1632 LOG(INFO
) << "claimed identity " << client_identity
1633 << " does NOT match any FCrDNS: ";
1634 for (auto const& client_fcrdns
: client_fcrdns_
) {
1635 LOG(INFO
) << " " << client_fcrdns
;
1639 // Bogus clients claim to be us or some local host.
1640 if (sock_
.has_peername() && ((client_identity
== server_identity_
) ||
1641 (client_identity
== "localhost") ||
1642 (client_identity
== "localhost.localdomain"))) {
1644 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1645 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1651 LOG(INFO
) << "allow-listed IP address can claim to be "
1656 // Ease up in test mode.
1657 if (FLAGS_test_mode
|| getenv("GHSMTP_TEST_MODE")) {
1661 error_msg
= fmt::format("liar, claimed to be {}", client_identity
.ascii());
1662 out_() << "550 5.7.1 liar\r\n" << std::flush
;
1666 std::vector
<std::string
> labels
;
1667 boost::algorithm::split(labels
, client_identity
.ascii(),
1668 boost::algorithm::is_any_of("."));
1669 if (labels
.size() < 2) {
1671 fmt::format("claimed bogus identity {}", client_identity
.ascii());
1672 out_() << "550 4.7.1 bogus identity\r\n" << std::flush
;
1674 // // Sometimes we may want to look at mail from non conforming
1675 // // sending systems.
1676 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1677 // << client_ << " claiming " << client_identity;
1681 if (lookup_domain(block_
, client_identity
)) {
1683 fmt::format("claimed blocked identity {}", client_identity
.ascii());
1684 out_() << "550 4.7.1 blocked identity\r\n" << std::flush
;
1688 auto const tld
{tld_db_
.get_registered_domain(client_identity
.ascii())};
1690 // Sometimes we may want to look at mail from misconfigured
1692 // LOG(WARNING) << "claimed identity has no registered domain";
1695 else if (block_
.contains(tld
)) {
1697 fmt::format("claimed identity has blocked registered domain {}", tld
);
1698 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush
;
1702 if (domain_blocked(res_
, client_identity
) ||
1703 (tld
&& domain_blocked(res_
, Domain(tld
)))) {
1705 fmt::format("claimed identity {} blocked", client_identity
.ascii());
1706 out_() << "550 4.7.1 blocked identity\r\n" << std::flush
;
1710 DNS::Query
q(res_
, DNS::RR_type::A
, client_identity
.ascii());
1711 if (!q
.has_record()) {
1712 LOG(WARNING
) << "claimed identity " << client_identity
.ascii()
1713 << " not DNS resolvable";
1716 // not otherwise objectionable
1720 // check sender from RFC5321 MAIL FROM:
1721 bool Session::verify_sender_(Mailbox
const& sender
, std::string
& error_msg
)
1723 do_spf_check_(sender
);
1725 std::string
const sender_str
{sender
};
1727 if (sender
.empty()) {
1729 // is used to send bounce messages.
1733 if (domain_blocked(res_
, sender
.domain())) {
1734 error_msg
= fmt::format("{} sender domain blocked", sender_str
);
1735 out_() << "550 5.1.8 " << error_msg
<< "\r\n" << std::flush
;
1739 auto bad_senders_db_name
= config_path_
/ "bad_senders";
1741 if (bad_senders
.open(bad_senders_db_name
) &&
1742 bad_senders
.contains(sender_str
)) {
1743 error_msg
= fmt::format("{} bad sender", sender_str
);
1744 out_() << "550 5.1.8 " << error_msg
<< "\r\n" << std::flush
;
1748 // We don't accept mail /from/ a domain we are expecting to accept
1749 // mail for on an external network connection.
1751 // if (sock_.them_address_literal() != sock_.us_address_literal()) {
1752 // if ((accept_domains_.is_open() &&
1753 // (accept_domains_.contains(sender.domain().ascii()) ||
1754 // accept_domains_.contains(sender.domain().utf8()))) ||
1755 // (sender.domain() == server_identity_)) {
1757 // // Ease up in test mode.
1758 // if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1761 // out_() << "550 5.7.1 liar\r\n" << std::flush;
1762 // error_msg = fmt::format("liar, claimed to be {}",
1763 // sender.domain().utf8()); return false;
1767 if (sender
.domain().is_address_literal()) {
1768 if (sender
.domain() != sock_
.them_address_literal()) {
1769 LOG(WARNING
) << "sender domain " << sender
.domain() << " does not match "
1770 << sock_
.them_address_literal();
1775 if (!verify_sender_domain_(sender
.domain(), error_msg
)) {
1782 // this sender is the RFC5321 MAIL FROM: domain part
1783 bool Session::verify_sender_domain_(Domain
const& sender
,
1784 std::string
& error_msg
)
1786 if (sender
.empty()) {
1788 // is used to send bounce messages.
1792 // Break sender domain into labels:
1794 std::vector
<std::string
> labels
;
1795 boost::algorithm::split(labels
, sender
.ascii(),
1796 boost::algorithm::is_any_of("."));
1798 if (labels
.size() < 2) { // This is not a valid domain.
1799 error_msg
= fmt::format("{} invalid syntax", sender
.ascii());
1800 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1804 if (lookup_domain(block_
, sender
)) {
1805 error_msg
= fmt::format("SPF sender domain ({}) is blocked",
1806 spf_sender_domain_
.ascii());
1807 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1811 if (spf_result_
== SPF::Result::PASS
) {
1812 if (allow_
.contains(spf_sender_domain_
.ascii())) {
1813 LOG(INFO
) << "sender " << spf_sender_domain_
.ascii() << " allowed";
1818 tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
1820 if (allow_
.contains(reg_dom
)) {
1821 LOG(INFO
) << "sender registered domain \"" << reg_dom
<< "\" allowed";
1827 LOG(INFO
) << "sender \"" << sender
<< "\" not disallowed";
1831 void Session::do_spf_check_(Mailbox
const& sender
)
1833 if (!sock_
.has_peername()) {
1834 auto const ip_addr
= "127.0.0.1"; // use localhost for local socket
1835 spf_received_
= fmt::format(
1836 "Received-SPF: pass ({}: allow-listed) client-ip={}; "
1837 "envelope-from={}; helo={};",
1838 server_id_(), ip_addr
, sender
.as_string(), client_identity_
.ascii());
1839 spf_sender_domain_
= "localhost";
1843 auto const spf_srv
= SPF::Server
{server_id_().c_str()};
1844 auto spf_request
= SPF::Request
{spf_srv
};
1846 if (IP4::is_address(sock_
.them_c_str())) {
1847 spf_request
.set_ipv4_str(sock_
.them_c_str());
1849 else if (IP6::is_address(sock_
.them_c_str())) {
1850 spf_request
.set_ipv6_str(sock_
.them_c_str());
1853 LOG(FATAL
) << "bogus address " << sock_
.them_address_literal() << ", "
1854 << sock_
.them_c_str();
1857 auto const from
{static_cast<std::string
>(sender
)};
1859 spf_request
.set_env_from(from
.c_str());
1860 spf_request
.set_helo_dom(client_identity_
.ascii().c_str());
1862 auto const spf_res
{SPF::Response
{spf_request
}};
1863 spf_result_
= spf_res
.result();
1864 spf_received_
= spf_res
.received_spf();
1865 spf_sender_domain_
= spf_request
.get_sender_dom();
1867 LOG(INFO
) << "spf_received_ == " << spf_received_
;
1869 if (spf_result_
== SPF::Result::FAIL
) {
1870 LOG(INFO
) << "FAIL " << spf_res
.header_comment();
1872 else if (spf_result_
== SPF::Result::NEUTRAL
) {
1873 LOG(INFO
) << "NEUTRAL " << spf_res
.header_comment();
1875 else if (spf_result_
== SPF::Result::PASS
) {
1876 LOG(INFO
) << "PASS " << spf_res
.header_comment();
1879 LOG(INFO
) << "INVALID/SOFTFAIL/NONE/xERROR " << server_id_().c_str();
1883 bool Session::verify_from_params_(parameters_t
const& parameters
)
1885 // Take a look at the optional parameters:
1886 for (auto const& [name
, value
] : parameters
) {
1887 if (iequal(name
, "BODY")) {
1888 if (iequal(value
, "8BITMIME")) {
1889 // everything is cool, this is our default...
1891 else if (iequal(value
, "7BIT")) {
1892 // nothing to see here, move along...
1894 else if (iequal(value
, "BINARYMIME")) {
1895 LOG(INFO
) << "using BINARYMIME";
1899 LOG(WARNING
) << "unrecognized BODY type \"" << value
<< "\" requested";
1902 else if (iequal(name
, "SMTPUTF8")) {
1903 if (!value
.empty()) {
1904 LOG(WARNING
) << "SMTPUTF8 parameter has a value: " << value
;
1908 else if (iequal(name
, "PRDR")) {
1909 LOG(INFO
) << "using PRDR";
1913 else if (iequal(name
, "SIZE")) {
1914 if (value
.empty()) {
1915 LOG(WARNING
) << "SIZE parameter has no value.";
1919 auto const sz
= stoull(value
);
1920 if (sz
> max_msg_size()) {
1921 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1922 LOG(WARNING
) << "SIZE parameter too large: " << sz
;
1926 catch (std::invalid_argument
const& e
) {
1927 LOG(WARNING
) << "SIZE parameter has invalid value: " << value
;
1929 catch (std::out_of_range
const& e
) {
1930 LOG(WARNING
) << "SIZE parameter has out-of-range value: " << value
;
1932 // I guess we just ignore bad size parameters.
1935 else if (iequal(name
, "REQUIRETLS")) {
1937 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush
;
1938 LOG(WARNING
) << "REQUIRETLS needed";
1943 LOG(WARNING
) << "unrecognized 'MAIL FROM' parameter " << name
<< "="
1951 bool Session::verify_rcpt_params_(parameters_t
const& parameters
)
1953 // Take a look at the optional parameters:
1954 for (auto const& [name
, value
] : parameters
) {
1955 if (iequal(name
, "RRVS")) {
1956 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1957 LOG(INFO
) << name
<< "=" << value
;
1960 LOG(WARNING
) << "unrecognized 'RCPT TO' parameter " << name
<< "="
1968 // check recipient from RFC5321 RCPT TO:
1969 bool Session::verify_recipient_(Mailbox
const& recipient
)
1971 if ((recipient
.local_part() == "Postmaster") && (recipient
.domain() == "")) {
1972 LOG(INFO
) << "magic Postmaster address";
1976 auto const accepted_domain
{[this, &recipient
] {
1977 if (recipient
.domain().is_address_literal()) {
1978 if (recipient
.domain() != sock_
.us_address_literal()) {
1979 LOG(WARNING
) << "recipient.domain address " << recipient
.domain()
1980 << " does not match ours " << sock_
.us_address_literal();
1988 // Domains we accept mail for.
1989 if (accept_domains_
.is_open()) {
1990 if (accept_domains_
.contains(recipient
.domain().ascii()) ||
1991 accept_domains_
.contains(recipient
.domain().utf8())) {
1996 // If we have no list of domains to accept, at least take our own.
1997 if (recipient
.domain() == server_id_()) {
2005 if (!accepted_domain
) {
2006 out_() << "550 5.7.1 relay access denied\r\n" << std::flush
;
2007 LOG(WARNING
) << "relay access denied for domain " << recipient
.domain();
2011 if (recipient
.local_part() == "gene" && client_fcrdns_
.size() &&
2012 client_fcrdns_
[0].ascii().ends_with("outlook.com")) {
2013 // Getting Spam'ed by MS
2014 if (reverse_path_
.empty() || (reverse_path_
.length() > 40) ||
2015 reverse_path_
.domain().ascii().ends_with(".onmicrosoft.com")) {
2016 std::string error_msg
= fmt::format("rejecting spammy message from {}",
2017 client_fcrdns_
[0].ascii());
2018 LOG(WARNING
) << error_msg
;
2019 out_() << "550 5.7.0 " << error_msg
<< "\r\n" << std::flush
;
2024 // Check for local addresses we reject.
2026 auto bad_recipients_db_name
= config_path_
/ "bad_recipients";
2027 CDB bad_recipients_db
;
2028 if (bad_recipients_db
.open(bad_recipients_db_name
) &&
2029 bad_recipients_db
.contains_lc(recipient
.local_part())) {
2030 out_() << "550 5.1.1 bad recipient " << recipient
<< "\r\n" << std::flush
;
2031 LOG(WARNING
) << "bad recipient " << recipient
;
2037 auto fail_db_name
= config_path_
/ "fail_554";
2038 if (fs::exists(fail_db_name
)) {
2040 if (fail_db
.open(fail_db_name
) &&
2041 fail_db
.contains(recipient
.local_part())) {
2042 out_() << "554 5.7.1 prohibited for policy reasons" << recipient
2045 LOG(WARNING
) << "fail_554 recipient " << recipient
;