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_
= Domain
{[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_str
)
407 Domain client_identity
{client_identity_str
};
409 last_in_group_(verb
);
412 if (client_identity_
!= client_identity
) {
413 client_identity_
= client_identity
;
415 std::string error_msg
;
416 if (!verify_client_(client_identity_
, error_msg
)) {
417 LOG(INFO
) << "client identity blocked: " << error_msg
;
418 bad_host_(error_msg
.c_str());
424 out_() << "250 " << server_id_() << "\r\n";
430 if (sock_
.has_peername()) {
431 out_() << "250-" << server_id_() << " at your service, " << client_
435 out_() << "250-" << server_id_() << "\r\n";
438 // LIMITS SMTP Service Extension,
439 // <https://www.rfc-editor.org/rfc/rfc9422.html>
440 out_() << "250-LIMITS RCPTMAX=" << Config::max_recipients_per_message
442 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
443 out_() << "250-8BITMIME\r\n"; // RFC 6152
445 if (FLAGS_use_rrvs
) {
446 out_() << "250-RRVS\r\n"; // RFC 7293
449 if (FLAGS_use_prdr
) {
450 out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
454 // Check sasl sources for auth types.
455 // out_() << "250-AUTH PLAIN\r\n";
456 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
459 // If we're not already TLS, offer TLS
460 out_() << "250-STARTTLS\r\n"; // RFC 3207
463 out_() << "250-ENHANCEDSTATUSCODES\r\n"; // RFC 2034
465 if (FLAGS_use_pipelining
) {
466 out_() << "250-PIPELINING\r\n"; // RFC 2920
469 if (FLAGS_use_binarymime
) {
470 out_() << "250-BINARYMIME\r\n"; // RFC 3030
473 if (FLAGS_use_chunking
) {
474 out_() << "250-CHUNKING\r\n"; // RFC 3030
477 if (FLAGS_use_smtputf8
) {
478 out_() << "250-SMTPUTF8\r\n"; // RFC 6531
481 out_() << "250 HELP\r\n";
484 out_() << std::flush
;
486 if (sock_
.has_peername()) {
487 if (std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
488 client_identity_
) != end(client_fcrdns_
)) {
489 LOG(INFO
) << verb
<< " " << client_identity_
<< " from "
490 << sock_
.them_address_literal();
493 LOG(INFO
) << verb
<< " " << client_identity_
<< " from " << client_
;
497 LOG(INFO
) << verb
<< " " << client_identity_
;
501 void Session::mail_from(Mailbox
&& reverse_path
, parameters_t
const& parameters
)
503 check_for_pipeline_error_("MAIL FROM");
506 case xact_step::helo
:
507 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
508 LOG(WARNING
) << "'MAIL FROM' before HELO/EHLO"
509 << (sock_
.has_peername() ? " from " : "") << client_
;
511 case xact_step::mail
: break;
512 case xact_step::rcpt
:
513 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
514 LOG(WARNING
) << "nested MAIL command"
515 << (sock_
.has_peername() ? " from " : "") << client_
;
517 case xact_step::data
:
518 case xact_step::bdat
:
519 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush
;
520 LOG(WARNING
) << "nested MAIL command"
521 << (sock_
.has_peername() ? " from " : "") << client_
;
523 case xact_step::rset
:
524 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
525 LOG(WARNING
) << "error state must be cleared with a RSET"
526 << (sock_
.has_peername() ? " from " : "") << client_
;
530 if (!verify_from_params_(parameters
)) {
534 if (!smtputf8_
&& !is_ascii(reverse_path
.local_part())) {
535 LOG(WARNING
) << "non ascii reverse_path \"" << reverse_path
536 << "\" without SMTPUTF8 paramater";
539 std::string error_msg
;
540 if (!verify_sender_(reverse_path
, error_msg
)) {
541 LOG(INFO
) << "verify sender failed: " << error_msg
;
542 bad_host_(error_msg
.c_str());
545 reverse_path_
= std::move(reverse_path
);
546 // fwd_path_.clear();
547 // fwd_from_.clear();
548 forward_path_
.clear();
549 out_() << "250 2.1.0 MAIL FROM OK\r\n";
550 // No flush RFC-2920 section 3.1, this could be part of a command group.
552 fmt::memory_buffer params
;
553 for (auto const& [name
, value
] : parameters
) {
554 fmt::format_to(std::back_inserter(params
), " {}", name
);
555 if (!value
.empty()) {
556 fmt::format_to(std::back_inserter(params
), "={}", value
);
559 LOG(INFO
) << "MAIL FROM:<" << reverse_path_
<< ">" << fmt::to_string(params
);
561 state_
= xact_step::rcpt
;
564 void Session::rcpt_to(Mailbox
&& forward_path
, parameters_t
const& parameters
)
566 check_for_pipeline_error_("RCPT TO");
569 case xact_step::helo
:
570 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
571 LOG(WARNING
) << "'RCPT TO' before HELO/EHLO"
572 << (sock_
.has_peername() ? " from " : "") << client_
;
574 case xact_step::mail
:
575 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
576 LOG(WARNING
) << "'RCPT TO' before 'MAIL FROM'"
577 << (sock_
.has_peername() ? " from " : "") << client_
;
579 case xact_step::rcpt
:
580 case xact_step::data
: break;
581 case xact_step::bdat
:
582 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
583 LOG(WARNING
) << "'RCPT TO' during BDAT transfer"
584 << (sock_
.has_peername() ? " from " : "") << client_
;
586 case xact_step::rset
:
587 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
588 LOG(WARNING
) << "error state must be cleared with a RSET"
589 << (sock_
.has_peername() ? " from " : "") << client_
;
593 if (!verify_rcpt_params_(parameters
))
596 if (!verify_recipient_(forward_path
))
599 if (!smtputf8_
&& !is_ascii(forward_path
.local_part())) {
600 LOG(WARNING
) << "non ascii forward_path \"" << forward_path
601 << "\" without SMTPUTF8 paramater";
604 if (forward_path_
.size() >= Config::max_recipients_per_message
) {
605 out_() << "452 4.5.3 too many recipients\r\n" << std::flush
;
606 LOG(WARNING
) << "too many recipients <" << forward_path
<< ">";
609 // no check for dups, postfix doesn't
610 forward_path_
.emplace_back(std::move(forward_path
));
612 Mailbox
const& rcpt_to_mbx
= forward_path_
.back();
614 LOG(INFO
) << "RCPT TO:<" << rcpt_to_mbx
<< ">";
616 // No flush RFC-2920 section 3.1, this could be part of a command group.
617 out_() << "250 2.1.5 RCPT TO OK\r\n";
619 state_
= xact_step::data
;
622 // The headers Return-Path:, Received-SPF:, and Received: are returned
625 std::string
Session::added_headers_(MessageStore
const& msg
)
627 auto const protocol
{[this]() {
628 if (sock_
.tls() && !extensions_
) {
629 LOG(WARNING
) << "TLS active without extensions";
631 // <https://www.iana.org/assignments/mail-parameters/mail-parameters.xhtml#mail-parameters-5>
633 return sock_
.tls() ? "UTF8SMTPS" : "UTF8SMTP";
634 else if (sock_
.tls())
636 else if (extensions_
)
642 fmt::memory_buffer headers
;
645 fmt::format_to(std::back_inserter(headers
), "Return-Path: <{}>\r\n",
646 reverse_path_
.as_string());
649 if (!spf_received_
.empty()) {
650 fmt::format_to(std::back_inserter(headers
), "{}\r\n", spf_received_
);
654 // <https://tools.ietf.org/html/rfc5321#section-4.4>
655 fmt::format_to(std::back_inserter(headers
), "Received: from {}",
656 client_identity_
.utf8());
657 if (sock_
.has_peername()) {
658 fmt::format_to(std::back_inserter(headers
), " ({})", client_
);
660 fmt::format_to(std::back_inserter(headers
), "\r\n\tby {} with {} id {}",
661 server_identity_
.utf8(), protocol
, msg
.id().as_string_view());
662 if (forward_path_
.size()) {
663 fmt::format_to(std::back_inserter(headers
), "\r\n\tfor <{}>",
664 forward_path_
[0].as_string());
665 // From <https://datatracker.ietf.org/doc/html/rfc5321#section-4.4>:
666 // “If the FOR clause appears, it MUST contain exactly one <path>
667 // entry, even when multiple RCPT commands have been given. Multiple
668 // <path>s raise some security issues and have been deprecated, see
670 // for (auto i = 1u; i < forward_path_.size(); ++i)
671 // fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
673 std::string
const tls_info
{sock_
.tls_info()};
674 if (tls_info
.length()) {
675 fmt::format_to(std::back_inserter(headers
), "\r\n\t({})", tls_info
);
677 fmt::format_to(std::back_inserter(headers
), ";\r\n\t{}\r\n",
678 msg
.when().as_string_view());
680 return fmt::to_string(headers
);
684 bool lookup_domain(CDB
& cdb
, Domain
const& domain
)
686 if (!domain
.empty()) {
687 if (cdb
.contains(domain
.ascii())) {
690 if (domain
.is_unicode() && cdb
.contains(domain
.utf8())) {
698 std::tuple
<Session::SpamStatus
, std::string
> Session::spam_status_()
700 if (spf_result_
== SPF::Result::FAIL
&& !ip_allowed_
)
701 return {SpamStatus::spam
, "SPF failed"};
703 // These should have already been rejected by verify_client_().
704 if ((reverse_path_
.domain().ascii() == "localhost.local") ||
705 (reverse_path_
.domain().ascii() == "localhost"))
706 return {SpamStatus::spam
, "bogus reverse_path"};
708 std::vector
<std::string
> why_ham
;
710 // Anything enciphered tastes a lot like ham.
712 why_ham
.emplace_back("they used TLS");
714 if (spf_result_
== SPF::Result::PASS
) {
715 if (lookup_domain(allow_
, spf_sender_domain_
)) {
716 why_ham
.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
717 spf_sender_domain_
.utf8()));
720 auto tld_dom
{tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
721 if (tld_dom
&& allow_
.contains(tld_dom
)) {
722 why_ham
.emplace_back(fmt::format(
723 "SPF sender registered domain ({}) is allowed", tld_dom
));
729 why_ham
.emplace_back(
730 fmt::format("FCrDNS (or it's registered domain) is allowed"));
732 if (!why_ham
.empty())
733 return {SpamStatus::ham
,
734 fmt::format("{}", fmt::join(std::begin(why_ham
), std::end(why_ham
),
737 return {SpamStatus::spam
, "it's not ham"};
740 static std::string
folder(Session::SpamStatus status
,
741 std::vector
<Mailbox
> const& forward_path
,
742 Mailbox
const& reverse_path
)
745 Mailbox("gene.hightower+caf_=forwarded-gmail=digilicious.com@gmail.com"))
748 if (reverse_path
== Mailbox("ietf-smtp-bounces@ietf.org"))
752 std::string_view local_part
;
753 std::string_view folder
;
756 assignment assignments
[] = {
757 {"bootstrappable", ".bootstrappable"},
758 {"coreboot.org", ".coreboot"},
760 {"dns-privacy", ".dns-privacy"},
761 {"dnsmasq", ".INBOX.DNSmasq"},
762 {"emailcore", ".emailcore"},
763 {"fucking-facebook", ".FB"},
764 {"gene-ebay", ".EBay"},
765 {"i-hate-facebook", ".FB"},
766 {"i-hate-linked-in", ".linkedin"},
767 {"mailop", ".INBOX.mailop"},
768 {"modelfkeyboards.com", ""},
769 {"nest", ".INBOX.Nest"},
770 {"opendmarc-dev", ".dmarc"},
771 {"opendmarc-users", ".dmarc"},
772 {"papasys.com", ".INBOX.PAPA"},
773 {"postmaster-rua", ".INBOX.rua"},
774 {"quic=ietf.org", ".INBOX.quic"},
775 {"shadowserver-reports@digilicious.com", ".INBOX.shadowserver"},
776 {"theatlantic.com", ""},
777 {"time-nutz", ".time-nutz"},
778 {"zfsonlinux.topicbox.com", ".INBOX.zfs"},
781 for (auto ass
: assignments
) {
782 if (iequal(forward_path
[0].local_part(), ass
.local_part
))
783 return std::string(ass
.folder
);
786 if (iends_with(forward_path
[0].local_part(), "-at-duck"))
789 if (status
== Session::SpamStatus::spam
)
795 bool Session::msg_new()
797 CHECK((state_
== xact_step::data
) || (state_
== xact_step::bdat
));
799 auto const& [status
, reason
]{spam_status_()};
801 LOG(INFO
) << ((status
== SpamStatus::ham
) ? "ham since " : "spam since ")
804 // All sources of ham get a fresh 5 minute timeout per message.
805 if (status
== SpamStatus::ham
) {
806 if ((!FLAGS_immortal
) && (getenv("GHSMTP_IMMORTAL") == nullptr))
810 msg_
= std::make_unique
<MessageStore
>();
812 if (!FLAGS_max_write
)
813 FLAGS_max_write
= max_msg_size();
816 msg_
->open(server_id_(), FLAGS_max_write
,
817 folder(status
, forward_path_
, reverse_path_
));
818 auto const hdrs
{added_headers_(*(msg_
.get()))};
821 // fmt::memory_buffer spam_status;
822 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
823 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
824 // msg_->write(spam_status.data(), spam_status.size());
826 LOG(INFO
) << "Spam-Status: "
827 << ((status
== SpamStatus::spam
) ? "Yes" : "No") << ", "
832 catch (std::system_error
const& e
) {
835 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
836 LOG(ERROR
) << "no space";
842 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
843 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
844 LOG(ERROR
) << e
.what();
850 catch (std::exception
const& e
) {
851 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
852 LOG(ERROR
) << e
.what();
858 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
859 LOG(ERROR
) << "msg_new failed with no exception caught";
865 bool Session::msg_write(char const* s
, std::streamsize count
)
867 if ((state_
!= xact_step::data
) && (state_
!= xact_step::bdat
))
874 if (msg_
->write(s
, count
))
877 catch (std::system_error
const& e
) {
880 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
881 LOG(ERROR
) << "no space";
887 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
888 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
889 LOG(ERROR
) << e
.what();
895 catch (std::exception
const& e
) {
896 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
897 LOG(ERROR
) << e
.what();
903 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
904 LOG(ERROR
) << "msg_write failed with no exception caught";
910 bool Session::data_start()
912 last_in_group_("DATA");
915 case xact_step::helo
:
916 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
917 LOG(WARNING
) << "'DATA' before HELO/EHLO"
918 << (sock_
.has_peername() ? " from " : "") << client_
;
920 case xact_step::mail
:
921 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
922 LOG(WARNING
) << "'DATA' before 'MAIL FROM'"
923 << (sock_
.has_peername() ? " from " : "") << client_
;
925 case xact_step::rcpt
:
927 /******************************************************************
928 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
930 The DATA command can fail at only two points in the protocol
933 If there was no MAIL, or no RCPT, command, or all such commands
934 were rejected, the server MAY return a "command out of sequence"
935 (503) or "no valid recipients" (554) reply in response to the DATA
936 command. If one of those replies (or any other 5yz reply) is
937 received, the client MUST NOT send the message data; more
938 generally, message data MUST NOT be sent unless a 354 reply is
941 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
943 The additional restriction is that when there have been no
944 successful RCPT commands in the mail transaction, the DATA command
945 MUST fail with a 503 reply code.
947 Therefore I will send the reply code (503) that is valid for both,
948 and do the same for the BDAT case.
949 *******************************************************************/
951 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
952 LOG(WARNING
) << "no valid recipients"
953 << (sock_
.has_peername() ? " from " : "") << client_
;
955 case xact_step::data
: break;
956 case xact_step::bdat
:
957 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
958 LOG(WARNING
) << "'DATA' during BDAT transfer"
959 << (sock_
.has_peername() ? " from " : "") << client_
;
961 case xact_step::rset
:
962 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
963 LOG(WARNING
) << "error state must be cleared with a RSET"
964 << (sock_
.has_peername() ? " from " : "") << client_
;
969 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
971 LOG(WARNING
) << "DATA does not support BINARYMIME";
972 state_
= xact_step::rset
; // RFC 3030 section 3 page 5
977 LOG(ERROR
) << "msg_new() failed";
981 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush
;
986 bool Session::do_deliver_()
994 catch (std::system_error
const& e
) {
997 out_() << "452 4.3.1 mail system full\r\n" << std::flush
;
998 LOG(ERROR
) << "no space";
1004 out_() << "451 4.3.0 mail system error\r\n" << std::flush
;
1006 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
1007 LOG(ERROR
) << e
.what();
1014 LOG(INFO
) << "message delivered, " << msg_
->size() << " octets, with id "
1019 void Session::xfer_response_(std::string_view success_msg
)
1021 auto bad_recipients_db_name
= config_path_
/ "bad_recipients_data";
1022 CDB bad_recipients_db
;
1023 if (!bad_recipients_db
.open(bad_recipients_db_name
)) {
1024 LOG(WARNING
) << "can't open bad_recipients_data";
1027 auto temp_fail_db_name
= config_path_
/ "temp_fail_data";
1029 if (!temp_fail_db
.open(temp_fail_db_name
)) {
1030 LOG(WARNING
) << "can't open temp_fail_data";
1033 std::vector
<std::string
> bad_recipients
;
1034 if (bad_recipients_db
.is_open()) {
1035 for (auto fp
: forward_path_
) {
1036 if (bad_recipients_db
.contains_lc(fp
.local_part())) {
1037 bad_recipients
.push_back(fp
);
1038 LOG(WARNING
) << "bad recipient " << fp
;
1042 std::vector
<std::string
> temp_failed
;
1043 if (temp_fail_db
.is_open()) {
1044 for (auto fp
: forward_path_
) {
1045 if (temp_fail_db
.contains_lc(fp
.local_part())) {
1046 temp_failed
.push_back(fp
);
1047 LOG(WARNING
) << "temp failed recipient " << fp
;
1052 if (prdr_
&& forward_path_
.size() > 1 &&
1053 (bad_recipients
.size() || temp_failed
.size())) {
1055 if (forward_path_
.size() == bad_recipients
.size()) {
1056 out_() << "550 5.1.1 all recipients bad\r\n";
1058 else if (forward_path_
.size() == temp_failed
.size()) {
1059 out_() << "450 4.1.1 temporary failure for all recipients\r\n";
1062 // this is the mixed situation
1063 out_() << "353 per recipient responses follow:\r\n";
1064 for (auto fp
: forward_path_
) {
1065 if (bad_recipients_db
.is_open() && bad_recipients_db
.contains_lc(fp
.local_part())) {
1066 out_() << "550 5.1.1 bad recipient " << fp
<< "\r\n";
1067 LOG(INFO
) << "bad recipient " << fp
;
1069 else if (temp_fail_db
.is_open() && temp_fail_db
.contains_lc(fp
.local_part())) {
1070 out_() << "450 4.1.1 temporary failure for " << fp
<< "\r\n";
1071 LOG(INFO
) << "temp fail for " << fp
;
1074 out_() << "250 2.0.0 success for " << fp
<< "\r\n";
1075 LOG(INFO
) << "success for " << fp
;
1079 // after the per recipient status, a final and I think useless message.
1080 if (forward_path_
.size() > (bad_recipients
.size() + temp_failed
.size())) {
1081 out_() << "250 2.0.0 success for some recipients\r\n";
1083 else if (temp_failed
.size()) {
1084 out_() << "450 4.1.1 temporary failure for some recipients\r\n";
1087 out_() << "550 5.1.1 some bad recipients\r\n";
1092 if (bad_recipients
.size()) {
1093 out_() << "550 5.1.1 bad recipient(s) ";
1094 std::copy(begin(bad_recipients
), end(bad_recipients
),
1095 std::experimental::make_ostream_joiner(out_(), ", "));
1098 else if (temp_failed
.size()) {
1099 out_() << "450 4.1.1 temporary failure for ";
1100 std::copy(begin(temp_failed
), end(temp_failed
),
1101 std::experimental::make_ostream_joiner(out_(), ", "));
1105 out_() << "250 2.0.0 " << success_msg
<< " OK\r\n";
1110 void Session::data_done()
1112 CHECK((state_
== xact_step::data
));
1114 if (msg_
&& msg_
->size_error()) {
1119 // Check for and act on magic "wait" address.
1121 using namespace boost::xpressive
;
1123 sregex
const rex
= icase("wait-data-") >> (secs_
= +_d
);
1126 for (auto fp
: forward_path_
) {
1127 if (regex_match(fp
.local_part(), what
, rex
) ||
1128 regex_match(fp
.local_part(), what
, all_rex
)) {
1129 auto const str
= what
[secs_
].str();
1130 LOG(INFO
) << "waiting at DATA " << str
<< " seconds";
1132 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1133 google::FlushLogFiles(google::INFO
);
1134 out_() << std::flush
;
1136 LOG(INFO
) << "done waiting";
1141 if (!do_deliver_()) {
1145 xfer_response_("DATA");
1147 out_() << std::flush
;
1151 void Session::data_size_error()
1153 out_().clear(); // clear possible eof from input side
1154 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1158 LOG(WARNING
) << "DATA size error";
1162 void Session::data_error()
1164 out_().clear(); // clear possible eof from input side
1165 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush
;
1169 LOG(WARNING
) << "DATA error";
1173 bool Session::bdat_start(size_t n
)
1175 // In practice, this one gets pipelined.
1176 // last_in_group_("BDAT");
1179 case xact_step::helo
:
1180 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
1181 LOG(WARNING
) << "'BDAT' before HELO/EHLO"
1182 << (sock_
.has_peername() ? " from " : "") << client_
;
1184 case xact_step::mail
:
1185 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
1186 LOG(WARNING
) << "'BDAT' before 'MAIL FROM'"
1187 << (sock_
.has_peername() ? " from " : "") << client_
;
1189 case xact_step::rcpt
:
1190 // See comment in data_start()
1191 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
1192 LOG(WARNING
) << "no valid recipients"
1193 << (sock_
.has_peername() ? " from " : "") << client_
;
1195 case xact_step::data
: // first bdat
1197 case xact_step::bdat
: return true;
1198 case xact_step::rset
:
1199 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
1200 LOG(WARNING
) << "error state must be cleared with a RSET"
1201 << (sock_
.has_peername() ? " from " : "") << client_
;
1205 state_
= xact_step::bdat
;
1210 void Session::bdat_done(size_t n
, bool last
)
1212 if (state_
!= xact_step::bdat
) {
1221 if (msg_
->size_error()) {
1227 out_() << "250 2.0.0 BDAT " << n
<< " OK\r\n" << std::flush
;
1228 LOG(INFO
) << "BDAT " << n
;
1232 LOG(INFO
) << "BDAT " << n
<< " LAST";
1234 // Check for and act on magic "wait" address.
1236 using namespace boost::xpressive
;
1238 sregex
const rex
= icase("wait-bdat-") >> (secs_
= +_d
);
1241 for (auto fp
: forward_path_
) {
1242 if (regex_match(fp
.local_part(), what
, rex
) ||
1243 regex_match(fp
.local_part(), what
, all_rex
)) {
1244 auto const str
= what
[secs_
].str();
1245 LOG(INFO
) << "waiting at BDAT " << str
<< " seconds";
1247 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1248 google::FlushLogFiles(google::INFO
);
1249 out_() << std::flush
;
1251 LOG(INFO
) << "done waiting";
1256 if (!do_deliver_()) {
1260 xfer_response_(fmt::format("BDAT {} LAST", n
));
1262 out_() << std::flush
;
1266 void Session::bdat_size_error()
1268 out_().clear(); // clear possible eof from input side
1269 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1273 LOG(WARNING
) << "BDAT size error";
1277 void Session::bdat_seq_error()
1279 out_().clear(); // clear possible eof from input side
1280 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush
;
1284 LOG(WARNING
) << "BDAT sequence error";
1288 void Session::bdat_io_error()
1290 out_().clear(); // clear possible eof from input side
1291 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush
;
1295 LOG(WARNING
) << "BDAT I/O error";
1299 void Session::rset()
1301 out_() << "250 2.1.5 RSET OK\r\n";
1302 // No flush RFC-2920 section 3.1, this could be part of a command group.
1303 LOG(INFO
) << "RSET";
1307 void Session::noop(std::string_view str
)
1309 last_in_group_("NOOP");
1310 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush
;
1311 LOG(INFO
) << "NOOP" << (str
.length() ? " " : "") << str
;
1314 void Session::vrfy(std::string_view str
)
1316 last_in_group_("VRFY");
1317 out_() << "252 2.1.5 try it\r\n" << std::flush
;
1318 LOG(INFO
) << "VRFY" << (str
.length() ? " " : "") << str
;
1321 void Session::help(std::string_view str
)
1323 if (iequal(str
, "help\r\n")) {
1324 out_() << "214 2.0.0 Now you're sounding desperate.\r\n" << std::flush
;
1327 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n"
1330 LOG(INFO
) << "HELP" << (str
.length() ? " " : "") << str
;
1333 void Session::quit()
1336 // last_in_group_("QUIT");
1337 out_() << "221 2.0.0 closing connection\r\n" << std::flush
;
1338 LOG(INFO
) << "QUIT";
1342 void Session::auth()
1344 out_() << "454 4.7.0 authentication failure\r\n" << std::flush
;
1345 LOG(INFO
) << "AUTH";
1349 void Session::error(std::string_view log_msg
)
1351 out_() << "421 4.3.5 system error: " << log_msg
<< "\r\n" << std::flush
;
1352 LOG(WARNING
) << log_msg
;
1355 void Session::cmd_unrecognized(std::string_view cmd
)
1357 auto const escaped
{esc(cmd
)};
1358 LOG(WARNING
) << "command unrecognized: \"" << escaped
<< "\"";
1360 if (++n_unrecognized_cmds_
>= Config::max_unrecognized_cmds
) {
1361 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1362 << "\" exceeds limit\r\n"
1364 LOG(WARNING
) << n_unrecognized_cmds_
1365 << " unrecognized commands is too many";
1369 out_() << "500 5.5.1 command unrecognized: \"" << escaped
<< "\"\r\n"
1373 void Session::bare_lf()
1375 // Error code used by Office 365.
1376 out_() << "554 5.6.11 bare LF\r\n" << std::flush
;
1377 LOG(WARNING
) << "bare LF";
1381 void Session::max_out()
1383 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1384 LOG(WARNING
) << "message size maxed out";
1388 void Session::time_out()
1390 out_() << "421 4.4.2 time-out\r\n" << std::flush
;
1391 LOG(WARNING
) << "time-out" << (sock_
.has_peername() ? " from " : "")
1396 void Session::starttls()
1398 last_in_group_("STARTTLS");
1400 out_() << "554 5.5.1 TLS already active\r\n" << std::flush
;
1401 LOG(WARNING
) << "STARTTLS issued with TLS already active";
1403 else if (!extensions_
) {
1404 out_() << "554 5.5.1 TLS not avaliable without using EHLO\r\n"
1406 LOG(WARNING
) << "STARTTLS issued without using EHLO";
1409 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush
;
1410 if (sock_
.starttls_server(config_path_
)) {
1412 max_msg_size(Config::max_msg_size_bro
);
1413 LOG(INFO
) << "STARTTLS " << sock_
.tls_info();
1416 LOG(INFO
) << "failed STARTTLS";
1421 void Session::exit_()
1423 // sock_.log_totals();
1425 timespec time_used
{};
1426 clock_gettime(CLOCK_PROCESS_CPUTIME_ID
, &time_used
);
1428 LOG(INFO
) << "CPU time " << time_used
.tv_sec
<< "." << std::setw(9)
1429 << std::setfill('0') << time_used
.tv_nsec
<< " seconds";
1431 std::exit(EXIT_SUCCESS
);
1434 /////////////////////////////////////////////////////////////////////////////
1436 // All of the verify_* functions send their own error messages back to
1437 // the client on failure, and return false.
1439 bool Session::verify_ip_address_(std::string
& error_msg
)
1441 auto ip_block_db_name
= config_path_
/ "ip-block";
1443 if (ip_block
.open(ip_block_db_name
) &&
1444 ip_block
.contains(sock_
.them_c_str())) {
1446 fmt::format("IP address {} on static blocklist", sock_
.them_c_str());
1447 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1451 client_fcrdns_
.clear();
1453 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1454 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1455 LOG(INFO
) << "loopback address allowed";
1457 client_fcrdns_
.emplace_back(Domain
{"localhost"});
1458 client_
= fmt::format("localhost {}", sock_
.them_address_literal());
1462 auto const fcrdns
= DNS::fcrdns(res_
, sock_
.them_c_str());
1463 for (auto const& fcr
: fcrdns
) {
1464 client_fcrdns_
.emplace_back(fcr
);
1467 if (IP::is_private(sock_
.them_address_literal())) {
1468 LOG(INFO
) << "private address allowed";
1470 client_
= sock_
.them_address_literal();
1474 if (!client_fcrdns_
.empty()) {
1475 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1476 sock_
.them_address_literal());
1478 for (auto const& client_fcrdns
: client_fcrdns_
) {
1479 if (allow_
.contains_lc(client_fcrdns
.ascii())) {
1480 LOG(INFO
) << "FCrDNS " << client_fcrdns
<< " allowed";
1481 fcrdns_allowed_
= true;
1484 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1486 if (allow_
.contains(tld
)) {
1487 LOG(INFO
) << "FCrDNS registered domain " << tld
<< " allowed";
1488 fcrdns_allowed_
= true;
1494 for (auto const& client_fcrdns
: client_fcrdns_
) {
1495 if (block_
.contains_lc(client_fcrdns
.ascii())) {
1497 fmt::format("FCrDNS {} on static blocklist", client_fcrdns
.ascii());
1498 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1502 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1504 if (block_
.contains(tld
)) {
1505 error_msg
= fmt::format(
1506 "FCrDNS registered domain {} on static blocklist", tld
);
1507 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1514 client_
= fmt::format("{}", sock_
.them_address_literal());
1517 if (IP4::is_address(sock_
.them_c_str())) {
1519 auto const reversed
{IP4::reverse(sock_
.them_c_str())};
1522 // Check with allow list.
1523 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1526 for (auto wl : Config::wls) {
1527 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1528 if (q.has_record()) {
1529 using namespace boost::xpressive;
1531 auto const as = q.get_strings()[0];
1532 LOG(INFO) << "on allow list " << wl << " as " << as;
1536 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1539 if (regex_match(as, what, rex)) {
1540 auto const x = what[x_].str();
1541 auto const y = what[y_].str();
1544 std::from_chars(y.data(), y.data() + y.size(), value);
1547 LOG(INFO) << "allowed";
1551 LOG(INFO) << "Any A record skips check on block list";
1557 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1558 std::shuffle(std::begin(Config::bls
), std::end(Config::bls
),
1561 for (auto bl
: Config::bls
) {
1562 DNS::Query
q(res_
, DNS::RR_type::A
, reversed
+ bl
);
1563 if (q
.has_record()) {
1564 const auto a_strings
= q
.get_strings();
1565 for (auto const& as
: a_strings
) {
1566 LOG(INFO
) << bl
<< " returned " << as
;
1568 for (auto const& as
: a_strings
) {
1569 if (as
== "127.0.0.1") {
1570 LOG(INFO
) << "Should never get 127.0.0.1, from " << bl
;
1572 else if (as
== "127.0.0.10" || as
== "127.0.0.11") {
1573 LOG(INFO
) << "PBL listed, ignoring " << bl
;
1575 else if (as
== "127.255.255.252") {
1576 LOG(INFO
) << "Typing error in DNSBL name " << bl
;
1578 else if (as
== "127.255.255.254") {
1579 LOG(INFO
) << "Anonymous query through public resolver " << bl
;
1581 else if (as
== "127.255.255.255") {
1582 LOG(INFO
) << "Excessive number of queries " << bl
;
1585 error_msg
= fmt::format("IP address {} blocked: {} returned {}",
1586 sock_
.them_c_str(), bl
, as
);
1587 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1593 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1596 LOG(INFO
) << "IP address okay";
1600 bool domain_blocked(DNS::Resolver
& res
, Domain
const& identity
)
1602 if (identity
.is_address_literal()) {
1603 // don't "domain block" address literals
1606 if (!identity
.ascii().empty()) {
1607 Domain lookup
{fmt::format("{}.dbl.spamhaus.org", identity
.ascii())};
1608 DNS::Query
q(res
, DNS::RR_type::A
, lookup
.ascii());
1609 if (q
.has_record()) {
1610 const auto a_strings
= q
.get_strings();
1611 for (auto const& as
: a_strings
) {
1612 if (istarts_with(as
, "127.0.1.")) {
1613 LOG(INFO
) << "Domain " << identity
<< " blocked by spamhaus, " << as
;
1622 // check the identity from HELO/EHLO
1623 bool Session::verify_client_(Domain
const& client_identity
,
1624 std::string
& error_msg
)
1626 if (!client_fcrdns_
.empty()) {
1627 if (auto id
= std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
1629 id
!= end(client_fcrdns_
)) {
1630 // If the HELO ident is one of the FCrDNS names...
1631 if (id
!= begin(client_fcrdns_
)) {
1632 // ...then rotate that one to the front of the list
1633 std::rotate(begin(client_fcrdns_
), id
, id
+ 1);
1635 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1636 sock_
.them_address_literal());
1639 LOG(INFO
) << "claimed identity " << client_identity
1640 << " does NOT match any FCrDNS: ";
1641 for (auto const& client_fcrdns
: client_fcrdns_
) {
1642 LOG(INFO
) << " " << client_fcrdns
;
1646 // Bogus clients claim to be us or some local host.
1647 if (sock_
.has_peername() &&
1648 ((client_identity
== server_identity_
) ||
1649 (client_identity
.ascii() == "localhost") ||
1650 (client_identity
.ascii() == "localhost.localdomain"))) {
1652 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1653 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1659 LOG(INFO
) << "allow-listed IP address can claim to be "
1664 // Ease up in test mode.
1665 if (FLAGS_test_mode
|| getenv("GHSMTP_TEST_MODE")) {
1669 error_msg
= fmt::format("liar, claimed to be {}", client_identity
.ascii());
1670 out_() << "550 5.7.1 liar\r\n" << std::flush
;
1674 std::vector
<std::string
> labels
;
1675 boost::algorithm::split(labels
, client_identity
.ascii(),
1676 boost::algorithm::is_any_of("."));
1677 if (labels
.size() < 2) {
1679 fmt::format("claimed bogus identity {}", client_identity
.ascii());
1680 out_() << "550 4.7.1 bogus identity\r\n" << std::flush
;
1682 // // Sometimes we may want to look at mail from non conforming
1683 // // sending systems.
1684 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1685 // << client_ << " claiming " << client_identity;
1689 if (lookup_domain(block_
, client_identity
)) {
1691 fmt::format("claimed blocked identity {}", client_identity
.ascii());
1692 out_() << "550 4.7.1 blocked identity\r\n" << std::flush
;
1696 auto const tld
{tld_db_
.get_registered_domain(client_identity
.ascii())};
1698 // Sometimes we may want to look at mail from misconfigured
1700 // LOG(WARNING) << "claimed identity has no registered domain";
1703 else if (block_
.contains(tld
)) {
1705 fmt::format("claimed identity has blocked registered domain {}", tld
);
1706 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush
;
1710 if (domain_blocked(res_
, client_identity
) ||
1711 (tld
&& domain_blocked(res_
, Domain(tld
)))) {
1713 fmt::format("claimed identity {} blocked", client_identity
.ascii());
1714 out_() << "550 4.7.1 blocked identity\r\n" << std::flush
;
1718 DNS::Query
q(res_
, DNS::RR_type::A
, client_identity
.ascii());
1719 if (!q
.has_record()) {
1720 LOG(WARNING
) << "claimed identity " << client_identity
.ascii()
1721 << " not DNS resolvable";
1724 // not otherwise objectionable
1728 // check sender from RFC5321 MAIL FROM:
1729 bool Session::verify_sender_(Mailbox
const& sender
, std::string
& error_msg
)
1731 do_spf_check_(sender
);
1733 std::string
const sender_str
{sender
};
1735 if (sender
.empty()) {
1737 // is used to send bounce messages.
1741 if (domain_blocked(res_
, sender
.domain())) {
1742 error_msg
= fmt::format("{} sender domain blocked", sender_str
);
1743 out_() << "550 5.1.8 " << error_msg
<< "\r\n" << std::flush
;
1747 auto bad_senders_db_name
= config_path_
/ "bad_senders";
1749 if (bad_senders
.open(bad_senders_db_name
) &&
1750 bad_senders
.contains(sender_str
)) {
1751 error_msg
= fmt::format("{} bad sender", sender_str
);
1752 out_() << "550 5.1.8 " << error_msg
<< "\r\n" << std::flush
;
1756 // We don't accept mail /from/ a domain we are expecting to accept
1757 // mail for on an external network connection.
1759 // if (sock_.them_address_literal() != sock_.us_address_literal()) {
1760 // if ((accept_domains_.is_open() &&
1761 // (accept_domains_.contains(sender.domain().ascii()) ||
1762 // accept_domains_.contains(sender.domain().utf8()))) ||
1763 // (sender.domain() == server_identity_)) {
1765 // // Ease up in test mode.
1766 // if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1769 // out_() << "550 5.7.1 liar\r\n" << std::flush;
1770 // error_msg = fmt::format("liar, claimed to be {}",
1771 // sender.domain().utf8()); return false;
1775 if (sender
.domain().is_address_literal()) {
1776 if (sender
.domain().ascii() != sock_
.them_address_literal()) {
1777 LOG(WARNING
) << "sender domain " << sender
.domain() << " does not match "
1778 << sock_
.them_address_literal();
1783 if (!verify_sender_domain_(sender
.domain(), error_msg
)) {
1784 LOG(INFO
) << "verify sender domain failed: " << error_msg
;
1791 // this sender is the RFC5321 MAIL FROM: domain part
1792 bool Session::verify_sender_domain_(Domain
const& sender
,
1793 std::string
& error_msg
)
1795 if (sender
.empty()) {
1797 // is used to send bounce messages.
1801 // Break sender domain into labels:
1803 std::vector
<std::string
> labels
;
1804 boost::algorithm::split(labels
, sender
.ascii(),
1805 boost::algorithm::is_any_of("."));
1807 if (labels
.size() < 2) { // This is not a valid domain.
1808 error_msg
= fmt::format("{} invalid syntax", sender
.ascii());
1809 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1813 if (lookup_domain(block_
, sender
)) {
1814 error_msg
= fmt::format("SPF sender domain ({}) is blocked",
1815 spf_sender_domain_
.ascii());
1816 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1820 if (spf_result_
== SPF::Result::PASS
) {
1821 if (allow_
.contains(spf_sender_domain_
.ascii())) {
1822 LOG(INFO
) << "sender " << spf_sender_domain_
.ascii() << " allowed";
1827 tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
1829 if (allow_
.contains(reg_dom
)) {
1830 LOG(INFO
) << "sender registered domain \"" << reg_dom
<< "\" allowed";
1836 LOG(INFO
) << "sender \"" << sender
<< "\" not disallowed";
1840 void Session::do_spf_check_(Mailbox
const& sender
)
1842 if (!sock_
.has_peername()) {
1843 spf_received_
= fmt::format("Received-SPF: pass ({}: allow-listed) "
1844 "client-ip={}; envelope-from={}; helo={};",
1845 server_id_(), "127.0.0.1", sender
.as_string(),
1846 client_identity_
.ascii());
1847 spf_sender_domain_
= Domain
{"localhost"};
1851 auto const spf_srv
= SPF::Server
{server_id_().c_str()};
1852 auto spf_request
= SPF::Request
{spf_srv
};
1854 if (IP4::is_address(sock_
.them_c_str())) {
1855 spf_request
.set_ipv4_str(sock_
.them_c_str());
1857 else if (IP6::is_address(sock_
.them_c_str())) {
1858 spf_request
.set_ipv6_str(sock_
.them_c_str());
1861 LOG(FATAL
) << "bogus address " << sock_
.them_address_literal() << ", "
1862 << sock_
.them_c_str();
1865 auto const from
{static_cast<std::string
>(sender
)};
1867 spf_request
.set_env_from(from
.c_str());
1868 spf_request
.set_helo_dom(client_identity_
.ascii().c_str());
1870 auto const spf_res
{SPF::Response
{spf_request
}};
1871 spf_result_
= spf_res
.result();
1872 spf_received_
= spf_res
.received_spf();
1873 spf_sender_domain_
= Domain
{spf_request
.get_sender_dom()};
1875 LOG(INFO
) << "spf_received_ == " << spf_received_
;
1877 if (spf_result_
== SPF::Result::FAIL
) {
1878 LOG(INFO
) << "FAIL " << spf_res
.header_comment();
1880 else if (spf_result_
== SPF::Result::NEUTRAL
) {
1881 LOG(INFO
) << "NEUTRAL " << spf_res
.header_comment();
1883 else if (spf_result_
== SPF::Result::PASS
) {
1884 LOG(INFO
) << "PASS " << spf_res
.header_comment();
1887 LOG(INFO
) << "INVALID/SOFTFAIL/NONE/xERROR " << server_id_().c_str();
1891 bool Session::verify_from_params_(parameters_t
const& parameters
)
1893 // Take a look at the optional parameters:
1894 for (auto const& [name
, value
] : parameters
) {
1895 if (iequal(name
, "BODY")) {
1896 if (iequal(value
, "8BITMIME")) {
1897 // everything is cool, this is our default...
1899 else if (iequal(value
, "7BIT")) {
1900 // nothing to see here, move along...
1902 else if (iequal(value
, "BINARYMIME")) {
1903 LOG(INFO
) << "using BINARYMIME";
1907 LOG(WARNING
) << "unrecognized BODY type \"" << value
<< "\" requested";
1910 else if (iequal(name
, "SMTPUTF8")) {
1911 if (!value
.empty()) {
1912 LOG(WARNING
) << "SMTPUTF8 parameter has a value: " << value
;
1916 else if (iequal(name
, "PRDR")) {
1917 LOG(INFO
) << "using PRDR";
1921 else if (iequal(name
, "SIZE")) {
1922 if (value
.empty()) {
1923 LOG(WARNING
) << "SIZE parameter has no value.";
1927 auto const sz
= stoull(value
);
1928 if (sz
> max_msg_size()) {
1929 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1930 LOG(WARNING
) << "SIZE parameter too large: " << sz
;
1934 catch (std::invalid_argument
const& e
) {
1935 LOG(WARNING
) << "SIZE parameter has invalid value: " << value
;
1937 catch (std::out_of_range
const& e
) {
1938 LOG(WARNING
) << "SIZE parameter has out-of-range value: " << value
;
1940 // I guess we just ignore bad size parameters.
1943 else if (iequal(name
, "REQUIRETLS")) {
1945 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush
;
1946 LOG(WARNING
) << "REQUIRETLS needed";
1951 LOG(WARNING
) << "unrecognized 'MAIL FROM' parameter " << name
<< "="
1959 bool Session::verify_rcpt_params_(parameters_t
const& parameters
)
1961 // Take a look at the optional parameters:
1962 for (auto const& [name
, value
] : parameters
) {
1963 if (iequal(name
, "RRVS")) {
1964 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1965 LOG(INFO
) << name
<< "=" << value
;
1968 LOG(WARNING
) << "unrecognized 'RCPT TO' parameter " << name
<< "="
1976 // check recipient from RFC5321 RCPT TO:
1977 bool Session::verify_recipient_(Mailbox
const& recipient
)
1979 if (recipient
== Mailbox
{"Postmaster"}) {
1980 LOG(INFO
) << "magic Postmaster address";
1984 auto const accepted_domain
{[this, &recipient
] {
1985 if (recipient
.domain().is_address_literal()) {
1986 if (recipient
.domain().ascii() != sock_
.us_address_literal()) {
1987 LOG(WARNING
) << "recipient.domain address " << recipient
.domain()
1988 << " does not match ours " << sock_
.us_address_literal();
1996 // Domains we accept mail for.
1997 if (accept_domains_
.is_open()) {
1998 if (accept_domains_
.contains(recipient
.domain().ascii()) ||
1999 accept_domains_
.contains(recipient
.domain().utf8())) {
2004 // If we have no list of domains to accept, at least take our own.
2005 if (recipient
.domain().ascii() == server_id_()) {
2013 if (!accepted_domain
) {
2014 out_() << "550 5.7.1 relay access denied\r\n" << std::flush
;
2015 LOG(WARNING
) << "relay access denied for domain " << recipient
.domain();
2019 // if (recipient.local_part() == "gene" && client_fcrdns_.size() &&
2020 // client_fcrdns_[0].ascii().ends_with("outlook.com")) {
2021 // // Getting Spam'ed by MS
2022 // if (reverse_path_.empty() || (reverse_path_.length() > 40) ||
2023 // reverse_path_.domain().ascii().ends_with(".onmicrosoft.com")) {
2024 // std::string error_msg = fmt::format("rejecting spammy message from {}",
2025 // client_fcrdns_[0].ascii());
2026 // LOG(WARNING) << error_msg;
2027 // out_() << "550 5.7.0 " << error_msg << "\r\n" << std::flush;
2032 // Check for local addresses we reject.
2034 auto bad_recipients_db_name
= config_path_
/ "bad_recipients";
2035 CDB bad_recipients_db
;
2036 if (bad_recipients_db
.open(bad_recipients_db_name
) &&
2037 bad_recipients_db
.contains_lc(recipient
.local_part())) {
2038 out_() << "550 5.1.1 bad recipient " << recipient
<< "\r\n" << std::flush
;
2039 LOG(WARNING
) << "bad recipient " << recipient
;
2045 auto fail_db_name
= config_path_
/ "fail_554";
2046 if (fs::exists(fail_db_name
)) {
2048 if (fail_db
.open(fail_db_name
) &&
2049 fail_db
.contains(recipient
.local_part())) {
2050 out_() << "554 5.7.1 prohibited for policy reasons" << recipient
2053 LOG(WARNING
) << "fail_554 recipient " << recipient
;