13 #include "MessageStore.hpp"
14 #include "Session.hpp"
17 #include "is_ascii.hpp"
20 #include <fmt/format.h>
21 #include <fmt/ostream.h>
23 #include <boost/algorithm/string/classification.hpp>
24 #include <boost/algorithm/string/split.hpp>
26 #include <boost/xpressive/xpressive.hpp>
30 #include <gflags/gflags.h>
32 using namespace std::string_literals
;
40 <https://www.dnswl.org/?page_id=15#query>
44 The return codes are structured as 127.0.x.y, with “x” indicating the category
45 of an entry and “y” indicating how trustworthy an entry has been judged.
47 Categories (127.0.X.y):
49 2 – Financial services
50 3 – Email Service Providers
51 4 – Organisations (both for-profit [ie companies] and non-profit)
52 5 – Service/network providers
53 6 – Personal/private servers
54 7 – Travel/leisure industry
55 8 – Public sector/governments
56 9 – Media and Tech companies
57 10 – some special cases
58 11 – Education, academic
60 13 – Manufacturing/Industrial
61 14 – Retail/Wholesale/Services
62 15 – Email Marketing Providers
63 20 – Added through Self Service without specific category
65 Trustworthiness / Score (127.0.x.Y):
67 0 = none – only avoid outright blocking (eg large ESP mailservers, -0.1)
68 1 = low – reduce chance of false positives (-1.0)
69 2 = medium – make sure to avoid false positives but allow override for clear
70 cases (-10.0) 3 = high – avoid override (-100.0).
72 The scores in parantheses are typical SpamAssassin scores.
74 Special return code 127.0.0.255
76 In cases where your nameserver issues more than 100’000 queries / 24 hours, you
77 may be blocked from further queries. The return code “127.0.0.255” indicates
83 "b.barracudacentral.org",
87 /*** Last octet from A record returned by blocklists ***
89 From <http://uribl.com/about.shtml#implementation>
92 ---------------------------------------------------------
93 1 00000001 Query blocked, possibly due to high volume
97 14 00001110 black,grey,red (for testpoints)
99 <https://www.spamhaus.org/faq/section/DNSBL%20Usage>
101 Spamhaus uses this general convention for return codes:
103 Return Code Description
104 127.0.0.0/24 Spamhaus IP Blocklists
105 127.0.1.0/24 Spamhaus Domain Blocklists
106 127.0.2.0/24 Spamhaus Zero Reputation Domains list
107 127.255.255.0/24 ERRORS (not implying a "listed" response)
109 Currently used return codes for Spamhaus public IP zones:
111 Return Code Zone Description
112 127.0.0.2 SBL Spamhaus SBL Data
113 127.0.0.3 SBL Spamhaus SBL CSS Data
114 127.0.0.4 XBL CBL Data
115 127.0.0.9 SBL Spamhaus DROP/EDROP Data
116 (in addition to 127.0.0.2, since 01-Jun-2016)
117 127.0.0.10 PBL ISP Maintained
118 127.0.0.11 PBL Spamhaus Maintained
120 127.0.0.5-7 are allocated to XBL for possible future use;
121 127.0.0.8 is allocated to SBL for possible future use.
123 From <https://www.spamhaus.org/faq/section/Spamhaus%20DBL#291>
125 Return Codes Data Source
126 127.0.1.2 spam domain
127 127.0.1.4 phish domain
128 127.0.1.5 malware domain
129 127.0.1.6 botnet C&C domain
130 127.0.1.102 abused legit spam
131 127.0.1.103 abused spammed redirector domain
132 127.0.1.104 abused legit phish
133 127.0.1.105 abused legit malware
134 127.0.1.106 abused legit botnet C&C
135 127.0.1.255 IP queries prohibited!
137 The following special codes indicate an error condition and should not
138 be taken to imply that the queried domain is "listed":
140 Return Code Description
141 127.255.255.252 Typing error in DNSBL name
142 127.255.255.254 Anonymous query through public resolver
143 127.255.255.255 Excessive number of queries
146 From <http://www.surbl.org/lists#multi>
148 last octet indicates which lists it belongs to. The bit positions in
149 that last octet for membership in the different lists are:
158 char const* uribls
[]{
163 constexpr auto greeting_wait
= std::chrono::seconds
{6};
164 constexpr int max_recipients_per_message
= 100;
165 constexpr int max_unrecognized_cmds
= 20;
167 // Read timeout value gleaned from RFC-1123 section 5.3.2 and RFC-5321
168 // section 4.5.3.2.7.
169 constexpr auto read_timeout
= std::chrono::minutes
{5};
170 constexpr auto write_timeout
= std::chrono::seconds
{30};
171 } // namespace Config
173 DEFINE_bool(immortal
, false, "don't set process timout");
175 DEFINE_uint64(max_read
, 0, "max data to read");
176 DEFINE_uint64(max_write
, 0, "max data to write");
178 DEFINE_bool(rrvs
, false, "support RRVS à la RFC 7293");
180 DEFINE_string(selector
, "ghsmtp", "DKIM selector");
182 DEFINE_bool(test_mode
, false, "ease up on some checks");
184 DEFINE_bool(use_pipelining
, true, "use PIPELINING extension");
186 boost::xpressive::mark_tag
secs_(1);
187 boost::xpressive::sregex
const all_rex
= boost::xpressive::icase("wait-all-") >>
188 (secs_
= +boost::xpressive::_d
);
190 Session::Session(fs::path config_path
,
191 std::function
<void(void)> read_hook
,
194 : config_path_(config_path
)
196 , sock_(fd_in
, fd_out
, read_hook
, Config::read_timeout
, Config::write_timeout
)
197 //, send_(config_path, "smtp")
198 //, srs_(config_path)
200 auto accept_db_name
= config_path_
/ "accept_domains";
201 auto allow_db_name
= config_path_
/ "allow";
202 auto block_db_name
= config_path_
/ "block";
203 auto forward_db_name
= config_path_
/ "forward";
205 accept_domains_
.open(accept_db_name
);
206 allow_
.open(allow_db_name
);
207 block_
.open(block_db_name
);
208 forward_
.open(forward_db_name
);
210 if (sock_
.has_peername() && !IP::is_private(sock_
.us_c_str())) {
211 auto fcrdns
= DNS::fcrdns(res_
, sock_
.us_c_str());
212 for (auto const& fcr
: fcrdns
) {
213 server_fcrdns_
.emplace_back(fcr
);
217 server_identity_
= [this] {
218 auto const id_from_env
{getenv("GHSMTP_SERVER_ID")};
220 return std::string
{id_from_env
};
222 auto const hostname
{osutil::get_hostname()};
223 if (hostname
.find('.') != std::string::npos
)
226 if (!server_fcrdns_
.empty()) {
227 // first result should be shortest
228 return server_fcrdns_
.front().ascii();
231 auto const us_c_str
= sock_
.us_c_str();
232 if (us_c_str
&& !IP::is_private(us_c_str
)) {
233 return IP::to_address_literal(us_c_str
);
236 LOG(FATAL
) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
240 // send_.set_sender(server_identity_);
242 max_msg_size(Config::max_msg_size_initial
);
245 void Session::max_msg_size(size_t max
)
247 max_msg_size_
= max
; // number to advertise via RFC 1870
249 if (FLAGS_max_read
) {
250 sock_
.set_max_read(FLAGS_max_read
);
253 auto const overhead
= std::max(max
/ 10, size_t(2048));
254 sock_
.set_max_read(max
+ overhead
);
258 void Session::bad_host_(char const* msg
) const
260 if (sock_
.has_peername()) {
261 // On my systems, this pattern triggers a fail2ban rule that
262 // blocks connections from this IP address on port 25 for a few
263 // days. See <https://www.fail2ban.org/> for more info.
264 syslog(LOG_MAIL
| LOG_WARNING
, "bad host [%s] %s", sock_
.them_c_str(), msg
);
266 std::exit(EXIT_SUCCESS
);
269 void Session::reset_()
271 // RSET does not force another EHLO/HELO, the one piece of per
272 // transaction data saved is client_identity_:
274 // client_identity_.clear(); <-- not cleared!
276 reverse_path_
.clear();
277 forward_path_
.clear();
278 spf_received_
.clear();
279 // fwd_path_.clear();
280 // fwd_from_.clear();
281 // rep_info_.clear();
291 max_msg_size(max_msg_size());
293 state_
= xact_step::mail
;
297 // Return codes from connection establishment are 220 or 554, according
298 // to RFC 5321. That's it.
300 void Session::greeting()
302 CHECK(state_
== xact_step::helo
);
304 if (sock_
.has_peername()) {
305 close(2); // if we're a networked program, never send to stderr
307 std::string error_msg
;
308 if (!verify_ip_address_(error_msg
)) {
309 LOG(INFO
) << "IP address blocked: " << error_msg
;
310 bad_host_(error_msg
.c_str());
313 /******************************************************************
314 <https://tools.ietf.org/html/rfc5321#section-4.3.1> says:
316 4.3. Sequencing of Commands and Replies
318 4.3.1. Sequencing Overview
320 The communication between the sender and receiver is an alternating
321 dialogue, controlled by the sender. As such, the sender issues a
322 command and the receiver responds with a reply. Unless other
323 arrangements are negotiated through service extensions, the sender
324 MUST wait for this response before sending further commands. One
325 important reply is the connection greeting. Normally, a receiver
326 will send a 220 "Service ready" reply when the connection is
327 completed. The sender SHOULD wait for this greeting message before
328 sending any commands.
332 “…the receiver responds with a reply.”
333 “…the sender MUST wait for this response…”
334 “One important reply is the connection greeting.”
335 “The sender SHOULD wait for this greeting…”
337 So is it MUST or SHOULD? I enforce MUST.
338 *******************************************************************/
340 // Wait a bit of time for pre-greeting traffic.
341 if (!(ip_allowed_
|| fcrdns_allowed_
)) {
342 if (sock_
.input_ready(Config::greeting_wait
)) {
343 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush
;
344 LOG(INFO
) << "input before any greeting from " << client_
;
345 bad_host_("input before any greeting");
347 // Give a half greeting and wait again.
348 out_() << "220-" << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush
;
349 if (sock_
.input_ready(Config::greeting_wait
)) {
350 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush
;
351 LOG(INFO
) << "input before full greeting from " << client_
;
352 bad_host_("input before full greeting");
355 LOG(INFO
) << "connect from " << client_
;
358 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush
;
360 if ((!FLAGS_immortal
) && (getenv("GHSMTP_IMMORTAL") == nullptr)) {
361 alarm(2 * 60); // initial alarm
365 void Session::flush() { out_() << std::flush
; }
367 void Session::last_in_group_(std::string_view verb
)
369 if (sock_
.input_ready(std::chrono::seconds(0))) {
370 LOG(WARNING
) << "pipelining error; input ready processing " << verb
;
374 void Session::check_for_pipeline_error_(std::string_view verb
)
376 if (!(FLAGS_use_pipelining
&& extensions_
)) {
377 if (sock_
.input_ready(std::chrono::seconds(0))) {
378 LOG(WARNING
) << "pipelining error; input ready processing " << verb
;
383 void Session::lo_(char const* verb
, std::string_view client_identity
)
385 last_in_group_(verb
);
388 if (client_identity_
!= client_identity
) {
389 client_identity_
= client_identity
;
391 std::string error_msg
;
392 if (!verify_client_(client_identity_
, error_msg
)) {
393 LOG(INFO
) << "client identity blocked: " << error_msg
;
394 bad_host_(error_msg
.c_str());
400 out_() << "250 " << server_id_() << "\r\n";
406 if (sock_
.has_peername()) {
407 out_() << "250-" << server_id_() << " at your service, " << client_
411 out_() << "250-" << server_id_() << "\r\n";
414 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
415 out_() << "250-8BITMIME\r\n"; // RFC 6152
418 out_() << "250-RRVS\r\n"; // RFC 7293
421 // out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
424 // Check sasl sources for auth types.
425 // out_() << "250-AUTH PLAIN\r\n";
426 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
429 // If we're not already TLS, offer TLS
430 out_() << "250-STARTTLS\r\n"; // RFC 3207
433 out_() << "250-ENHANCEDSTATUSCODES\r\n"; // RFC 2034
435 if (FLAGS_use_pipelining
) {
436 out_() << "250-PIPELINING\r\n"; // RFC 2920
439 out_() << "250-BINARYMIME\r\n"; // RFC 3030
440 out_() << "250-CHUNKING\r\n"; // RFC 3030
441 out_() << "250 SMTPUTF8\r\n"; // RFC 6531
444 out_() << std::flush
;
446 if (sock_
.has_peername()) {
447 if (std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
448 client_identity_
) != end(client_fcrdns_
)) {
449 LOG(INFO
) << verb
<< " " << client_identity
<< " from "
450 << sock_
.them_address_literal();
453 LOG(INFO
) << verb
<< " " << client_identity
<< " from " << client_
;
457 LOG(INFO
) << verb
<< " " << client_identity
;
461 void Session::mail_from(Mailbox
&& reverse_path
, parameters_t
const& parameters
)
463 check_for_pipeline_error_("MAIL FROM");
466 case xact_step::helo
:
467 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
468 LOG(WARNING
) << "'MAIL FROM' before HELO/EHLO"
469 << (sock_
.has_peername() ? " from " : "") << client_
;
471 case xact_step::mail
: break;
472 case xact_step::rcpt
:
473 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
474 LOG(WARNING
) << "nested MAIL command"
475 << (sock_
.has_peername() ? " from " : "") << client_
;
477 case xact_step::data
:
478 case xact_step::bdat
:
479 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush
;
480 LOG(WARNING
) << "nested MAIL command"
481 << (sock_
.has_peername() ? " from " : "") << client_
;
483 case xact_step::rset
:
484 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
485 LOG(WARNING
) << "error state must be cleared with a RSET"
486 << (sock_
.has_peername() ? " from " : "") << client_
;
490 if (!verify_from_params_(parameters
)) {
494 if (!smtputf8_
&& !is_ascii(reverse_path
.local_part())) {
495 LOG(WARNING
) << "non ascii reverse_path \"" << reverse_path
496 << "\" without SMTPUTF8 paramater";
499 std::string error_msg
;
500 if (!verify_sender_(reverse_path
, error_msg
)) {
501 LOG(INFO
) << "verify sender failed: " << error_msg
;
502 bad_host_(error_msg
.c_str());
505 reverse_path_
= std::move(reverse_path
);
506 // fwd_path_.clear();
507 // fwd_from_.clear();
508 forward_path_
.clear();
509 out_() << "250 2.1.0 MAIL FROM OK\r\n";
510 // No flush RFC-2920 section 3.1, this could be part of a command group.
512 fmt::memory_buffer params
;
513 for (auto const& [name
, value
] : parameters
) {
514 fmt::format_to(params
, " {}", name
);
515 if (!value
.empty()) {
516 fmt::format_to(params
, "={}", value
);
519 LOG(INFO
) << "MAIL FROM:<" << reverse_path_
<< ">" << fmt::to_string(params
);
521 state_
= xact_step::rcpt
;
524 // bool Session::forward_to_(std::string const& forward, Mailbox const& rcpt_to)
526 // // If we're already forwarding or replying, reject
527 // if (!fwd_path_.empty() || !rep_info_.empty()) {
528 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
531 // LOG(WARNING) << "failed to forward to <" << forward
532 // << "> already forwarding or replying for: " << rcpt_to;
536 // fwd_path_ = Mailbox(forward);
537 // fwd_from_ = rcpt_to;
539 // // New bounce address
540 // Reply::from_to bounce;
541 // bounce.mail_from = reverse_path_.as_string();
543 // auto const new_bounce = srs_.enc_bounce(bounce, server_id_().c_str());
545 // auto const mail_from = Mailbox(new_bounce);
547 // std::string error_msg;
548 // if (!send_.mail_from_rcpt_to(res_, mail_from, fwd_path_, error_msg)) {
549 // out_() << error_msg << std::flush;
550 // LOG(WARNING) << "failed to forward <" << fwd_path_ << "> " << error_msg;
554 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> forwarding to == <" << fwd_path_
559 // bool Session::reply_to_(Reply::from_to const& reply_info, Mailbox const&
562 // // If we're already forwarding or replying, reject
563 // if (!fwd_path_.empty() || !rep_info_.empty()) {
564 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
567 // LOG(WARNING) << "failed to reply to <" << reply_info.mail_from
568 // << "> already forwarding or replying for: " << rcpt_to;
572 // rep_info_ = reply_info;
574 // Mailbox const from(rep_info_.rcpt_to_local_part, server_identity_);
575 // Mailbox const to(rep_info_.mail_from);
577 // std::string error_msg;
578 // if (!send_.mail_from_rcpt_to(res_, from, to, error_msg)) {
579 // out_() << error_msg << std::flush;
580 // LOG(WARNING) << "failed to reply from <" << from << "> to <" << to << ">
586 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> is a reply to "
587 // << rep_info_.mail_from << " from " <<
588 // rep_info_.rcpt_to_local_part;
592 void Session::rcpt_to(Mailbox
&& forward_path
, parameters_t
const& parameters
)
594 check_for_pipeline_error_("RCPT TO");
597 case xact_step::helo
:
598 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
599 LOG(WARNING
) << "'RCPT TO' before HELO/EHLO"
600 << (sock_
.has_peername() ? " from " : "") << client_
;
602 case xact_step::mail
:
603 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
604 LOG(WARNING
) << "'RCPT TO' before 'MAIL FROM'"
605 << (sock_
.has_peername() ? " from " : "") << client_
;
607 case xact_step::rcpt
:
608 case xact_step::data
: break;
609 case xact_step::bdat
:
610 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
611 LOG(WARNING
) << "'RCPT TO' during BDAT transfer"
612 << (sock_
.has_peername() ? " from " : "") << client_
;
614 case xact_step::rset
:
615 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
616 LOG(WARNING
) << "error state must be cleared with a RSET"
617 << (sock_
.has_peername() ? " from " : "") << client_
;
621 if (!verify_rcpt_params_(parameters
))
624 if (!verify_recipient_(forward_path
))
627 if (!smtputf8_
&& !is_ascii(forward_path
.local_part())) {
628 LOG(WARNING
) << "non ascii forward_path \"" << forward_path
629 << "\" without SMTPUTF8 paramater";
632 if (forward_path_
.size() >= Config::max_recipients_per_message
) {
633 out_() << "452 4.5.3 too many recipients\r\n" << std::flush
;
634 LOG(WARNING
) << "too many recipients <" << forward_path
<< ">";
637 // no check for dups, postfix doesn't
638 forward_path_
.emplace_back(std::move(forward_path
));
640 Mailbox
const& rcpt_to_mbx
= forward_path_
.back();
642 LOG(INFO
) << "RCPT TO:<" << rcpt_to_mbx
<< ">";
644 // auto const rcpt_to_str = rcpt_to_mbx.as_string();
646 // if (auto reply = srs_.dec_reply(rcpt_to_mbx.local_part()); reply) {
647 // if (!reply_to_(*reply, rcpt_to_mbx))
650 // else if (auto const forward = forward_.find(rcpt_to_str.c_str()); forward)
652 // if (!forward_to_(*forward, rcpt_to_mbx))
656 // LOG(INFO) << "RCPT TO:<" << rcpt_to_str << ">";
659 // No flush RFC-2920 section 3.1, this could be part of a command group.
660 out_() << "250 2.1.5 RCPT TO OK\r\n";
662 state_
= xact_step::data
;
665 // The headers Return-Path:, Received-SPF:, and Received: are returned
668 std::string
Session::added_headers_(MessageStore
const& msg
)
670 auto const protocol
{[this]() {
671 if (sock_
.tls() && !extensions_
) {
672 LOG(WARNING
) << "TLS active without extensions";
674 // <https://www.iana.org/assignments/mail-parameters/mail-parameters.xhtml#mail-parameters-5>
676 return sock_
.tls() ? "UTF8SMTPS" : "UTF8SMTP";
677 else if (sock_
.tls())
679 else if (extensions_
)
685 fmt::memory_buffer headers
;
688 fmt::format_to(headers
, "Return-Path: <{}>\r\n", reverse_path_
);
691 if (!spf_received_
.empty()) {
692 fmt::format_to(headers
, "{}\r\n", spf_received_
);
696 // <https://tools.ietf.org/html/rfc5321#section-4.4>
697 fmt::format_to(headers
, "Received: from {}", client_identity_
.utf8());
698 if (sock_
.has_peername()) {
699 fmt::format_to(headers
, " ({})", client_
);
701 fmt::format_to(headers
, "\r\n\tby {} with {} id {}", server_identity_
.utf8(),
703 if (forward_path_
.size()) {
704 fmt::format_to(headers
, "\r\n\tfor <{}>", forward_path_
[0]);
705 for (auto i
= 1u; i
< forward_path_
.size(); ++i
)
706 fmt::format_to(headers
, ",\r\n\t <{}>", forward_path_
[i
]);
708 std::string
const tls_info
{sock_
.tls_info()};
709 if (tls_info
.length()) {
710 fmt::format_to(headers
, "\r\n\t({})", tls_info
);
712 fmt::format_to(headers
, ";\r\n\t{}\r\n", msg
.when());
714 return fmt::to_string(headers
);
718 bool lookup_domain(CDB
& cdb
, Domain
const& domain
)
720 if (!domain
.empty()) {
721 if (cdb
.contains(domain
.ascii())) {
724 if (domain
.is_unicode() && cdb
.contains(domain
.utf8())) {
732 std::tuple
<Session::SpamStatus
, std::string
> Session::spam_status_()
734 if (spf_result_
== SPF::Result::FAIL
&& !ip_allowed_
)
735 return {SpamStatus::spam
, "SPF failed"};
737 // These should have already been rejected by verify_client_().
738 if ((reverse_path_
.domain() == "localhost.local") ||
739 (reverse_path_
.domain() == "localhost"))
740 return {SpamStatus::spam
, "bogus reverse_path"};
742 std::vector
<std::string
> why_ham
;
744 // Anything enciphered tastes a lot like ham.
746 why_ham
.emplace_back("they used TLS");
748 if (spf_result_
== SPF::Result::PASS
) {
749 if (lookup_domain(allow_
, spf_sender_domain_
)) {
750 why_ham
.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
751 spf_sender_domain_
.utf8()));
754 auto tld_dom
{tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
755 if (tld_dom
&& allow_
.contains(tld_dom
)) {
756 why_ham
.emplace_back(fmt::format(
757 "SPF sender registered domain ({}) is allowed", tld_dom
));
763 why_ham
.emplace_back(
764 fmt::format("FCrDNS (or it's registered domain) is allowed"));
766 if (!why_ham
.empty())
767 return {SpamStatus::ham
,
768 fmt::format("{}", fmt::join(std::begin(why_ham
), std::end(why_ham
),
771 return {SpamStatus::spam
, "it's not ham"};
774 static std::string
folder(Session::SpamStatus status
,
775 std::vector
<Mailbox
> const& forward_path
,
776 Mailbox
const& reverse_path
)
779 Mailbox("gene.hightower+caf_=forwarded-gmail=digilicious.com@gmail.com"))
782 if (status
== Session::SpamStatus::spam
)
785 if (iends_with(forward_path
[0].local_part(), "-at-duck"))
791 bool Session::msg_new()
793 CHECK((state_
== xact_step::data
) || (state_
== xact_step::bdat
));
795 auto const& [status
, reason
]{spam_status_()};
797 LOG(INFO
) << ((status
== SpamStatus::ham
) ? "ham since " : "spam since ")
800 // All sources of ham get a fresh 5 minute timeout per message.
801 if (status
== SpamStatus::ham
) {
802 if ((!FLAGS_immortal
) && (getenv("GHSMTP_IMMORTAL") == nullptr))
806 msg_
= std::make_unique
<MessageStore
>();
808 if (!FLAGS_max_write
)
809 FLAGS_max_write
= max_msg_size();
812 msg_
->open(server_id_(), FLAGS_max_write
,
813 folder(status
, forward_path_
, reverse_path_
));
814 auto const hdrs
{added_headers_(*(msg_
.get()))};
817 // fmt::memory_buffer spam_status;
818 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
819 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
820 // msg_->write(spam_status.data(), spam_status.size());
822 LOG(INFO
) << "Spam-Status: "
823 << ((status
== SpamStatus::spam
) ? "Yes" : "No") << ", "
828 catch (std::system_error
const& e
) {
831 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
832 LOG(ERROR
) << "no space";
838 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
839 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
840 LOG(ERROR
) << e
.what();
846 catch (std::exception
const& e
) {
847 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
848 LOG(ERROR
) << e
.what();
854 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
855 LOG(ERROR
) << "msg_new failed with no exception caught";
861 bool Session::msg_write(char const* s
, std::streamsize count
)
863 if ((state_
!= xact_step::data
) && (state_
!= xact_step::bdat
))
870 if (msg_
->write(s
, count
))
873 catch (std::system_error
const& e
) {
876 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush
;
877 LOG(ERROR
) << "no space";
883 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
884 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
885 LOG(ERROR
) << e
.what();
891 catch (std::exception
const& e
) {
892 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
893 LOG(ERROR
) << e
.what();
899 out_() << "451 4.0.0 mail system error\r\n" << std::flush
;
900 LOG(ERROR
) << "msg_write failed with no exception caught";
906 bool Session::data_start()
908 last_in_group_("DATA");
911 case xact_step::helo
:
912 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
913 LOG(WARNING
) << "'DATA' before HELO/EHLO"
914 << (sock_
.has_peername() ? " from " : "") << client_
;
916 case xact_step::mail
:
917 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
918 LOG(WARNING
) << "'DATA' before 'MAIL FROM'"
919 << (sock_
.has_peername() ? " from " : "") << client_
;
921 case xact_step::rcpt
:
923 /******************************************************************
924 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
926 The DATA command can fail at only two points in the protocol exchange:
928 If there was no MAIL, or no RCPT, command, or all such commands were
929 rejected, the server MAY return a "command out of sequence" (503) or
930 "no valid recipients" (554) reply in response to the DATA command.
932 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
934 The additional restriction is that when there have been no successful
935 RCPT commands in the mail transaction, the DATA command MUST fail
936 with a 503 reply code.
938 Therefore I will send the reply code that is valid for both, and
939 do the same for the BDAT case.
940 *******************************************************************/
942 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
943 LOG(WARNING
) << "no valid recipients"
944 << (sock_
.has_peername() ? " from " : "") << client_
;
946 case xact_step::data
: break;
947 case xact_step::bdat
:
948 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush
;
949 LOG(WARNING
) << "'DATA' during BDAT transfer"
950 << (sock_
.has_peername() ? " from " : "") << client_
;
952 case xact_step::rset
:
953 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
954 LOG(WARNING
) << "error state must be cleared with a RSET"
955 << (sock_
.has_peername() ? " from " : "") << client_
;
960 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
962 LOG(WARNING
) << "DATA does not support BINARYMIME";
963 state_
= xact_step::rset
; // RFC 3030 section 3 page 5
968 LOG(ERROR
) << "msg_new() failed";
972 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush
;
977 // bool Session::do_forward_(message::parsed& msg)
979 // auto msg_fwd = msg;
981 // // Generate a reply address
982 // Reply::from_to reply;
983 // reply.mail_from = msg_fwd.dmarc_from;
984 // reply.rcpt_to_local_part = fwd_from_.local_part();
986 // auto const reply_addr =
987 // fmt::format("{}@{}", srs_.enc_reply(reply), server_id_());
989 // auto const munging = false;
991 // auto const sender = server_identity_.ascii().c_str();
992 // auto const selector = FLAGS_selector.c_str();
993 // auto const key_file =
994 // (config_path_ / FLAGS_selector).replace_extension("private");
995 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
998 // auto const from_hdr =
999 // fmt::format("From: \"{} via\" <@>", msg_fwd.dmarc_from, reply_addr);
1000 // message::rewrite_from_to(msg_fwd, from_hdr, "", sender, selector,
1004 // auto const reply_to_hdr = fmt::format("Reply-To: {}", reply_addr);
1005 // message::rewrite_from_to(msg_fwd, "", reply_to_hdr, sender, selector,
1010 // if (!send_.send(msg_fwd.as_string())) {
1011 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1015 // LOG(ERROR) << "failed to send for " << fwd_path_;
1019 // LOG(INFO) << "successfully sent for " << fwd_path_;
1023 // bool Session::do_reply_(message::parsed& msg)
1025 // Mailbox to_mbx(rep_info_.mail_from);
1026 // Mailbox from_mbx(rep_info_.rcpt_to_local_part, server_identity_);
1028 // auto reply = std::make_unique<MessageStore>();
1029 // reply->open(server_id_(), FLAGS_max_write, ".Drafts");
1031 // auto const date{Now{}};
1032 // auto const pill{Pill{}};
1033 // auto const mid_str =
1034 // fmt::format("<{}.{}@{}>", date.sec(), pill, server_identity_);
1036 // fmt::memory_buffer bfr;
1038 // fmt::format_to(bfr, "From: <{}>\r\n", from_mbx);
1039 // fmt::format_to(bfr, "To: <{}>\r\n", to_mbx);
1041 // fmt::format_to(bfr, "Date: {}\r\n", date.c_str());
1043 // fmt::format_to(bfr, "Message-ID: {}\r\n", mid_str.c_str());
1045 // if (!msg.get_header(message::Subject).empty()) {
1046 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
1047 // msg.get_header(message::Subject));
1050 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
1051 // "Reply to your message");
1054 // if (!msg.get_header(message::In_Reply_To).empty()) {
1055 // fmt::format_to(bfr, "{}: {}\r\n", message::In_Reply_To,
1056 // msg.get_header(message::In_Reply_To));
1059 // if (!msg.get_header(message::MIME_Version).empty() &&
1060 // msg.get_header(message::Content_Type).empty()) {
1061 // fmt::format_to(bfr, "{}: {}\r\n", message::MIME_Version,
1062 // msg.get_header(message::MIME_Version));
1063 // fmt::format_to(bfr, "{}: {}\r\n", message::Content_Type,
1064 // msg.get_header(message::Content_Type));
1067 // reply->write(fmt::to_string(bfr));
1069 // if (!msg.body.empty()) {
1070 // reply->write("\r\n");
1071 // reply->write(msg.body);
1074 // auto const msg_data = reply->freeze();
1075 // message::parsed msg_reply;
1076 // CHECK(msg_reply.parse(msg_data));
1078 // auto const sender = server_identity_.ascii().c_str();
1079 // auto const selector = FLAGS_selector.c_str();
1080 // auto const key_file =
1081 // (config_path_ / FLAGS_selector).replace_extension("private");
1082 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1084 // message::dkim_sign(msg_reply, sender, selector, key_file);
1086 // if (!send_.send(msg_reply.as_string())) {
1087 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1091 // LOG(ERROR) << "send failed for reply to " << to_mbx << " from " <<
1092 // from_mbx; return false;
1095 // LOG(INFO) << "successful reply to " << to_mbx << " from " << from_mbx;
1099 bool Session::do_deliver_()
1103 // auto const sender = server_identity_.ascii().c_str();
1104 // auto const selector = FLAGS_selector.c_str();
1105 // auto const key_file =
1106 // (config_path_ / FLAGS_selector).replace_extension("private");
1107 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1110 // auto const msg_data = msg_->freeze();
1112 // message::parsed msg;
1114 // // Only deal in RFC-5322 Mail Objects.
1115 // bool const message_parsed = msg.parse(msg_data);
1116 // if (message_parsed) {
1118 // // remove any Return-Path
1119 // message::remove_delivery_headers(msg);
1121 // auto const authentic =
1122 // message_parsed &&
1123 // message::authentication(msg, sender, selector, key_file);
1125 // // write a new Return-Path
1126 // msg_->write(fmt::format("Return-Path: <{}>\r\n", reverse_path_));
1128 // for (auto const h : msg.headers) {
1129 // msg_->write(h.as_string());
1130 // msg_->write("\r\n");
1132 // if (!msg.body.empty()) {
1133 // msg_->write("\r\n");
1134 // msg_->write(msg.body);
1139 // if (authentic && !fwd_path_.empty()) {
1140 // if (!do_forward_(msg))
1143 // if (authentic && !rep_info_.empty()) {
1144 // if (!do_reply_(msg))
1151 catch (std::system_error
const& e
) {
1154 out_() << "452 4.3.1 mail system full\r\n" << std::flush
;
1155 LOG(ERROR
) << "no space";
1161 out_() << "550 5.0.0 mail system error\r\n" << std::flush
;
1163 LOG(ERROR
) << "errno==" << errno
<< ": " << strerror(errno
);
1164 LOG(ERROR
) << e
.what();
1174 void Session::data_done()
1176 CHECK((state_
== xact_step::data
));
1178 if (msg_
&& msg_
->size_error()) {
1184 // out_() << "353\r\n";
1185 // for (auto fp : forward_path_) {
1186 // out_() << "250 2.1.5 RCPT TO OK\r\n";
1190 // Check for and act on magic "wait" address.
1192 using namespace boost::xpressive
;
1194 sregex
const rex
= icase("wait-data-") >> (secs_
= +_d
);
1197 for (auto fp
: forward_path_
) {
1198 if (regex_match(fp
.local_part(), what
, rex
) ||
1199 regex_match(fp
.local_part(), what
, all_rex
)) {
1200 auto const str
= what
[secs_
].str();
1201 LOG(INFO
) << "waiting at DATA " << str
<< " seconds";
1203 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1204 google::FlushLogFiles(google::INFO
);
1205 out_() << std::flush
;
1207 LOG(INFO
) << "done waiting";
1214 out_() << "250 2.0.0 DATA OK\r\n" << std::flush
;
1215 LOG(INFO
) << "message delivered, " << msg_
->size() << " octets, with id "
1220 void Session::data_size_error()
1222 out_().clear(); // clear possible eof from input side
1223 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1227 LOG(WARNING
) << "DATA size error";
1231 void Session::data_error()
1233 out_().clear(); // clear possible eof from input side
1234 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush
;
1238 LOG(WARNING
) << "DATA error";
1242 bool Session::bdat_start(size_t n
)
1244 // In practice, this one gets pipelined.
1245 // last_in_group_("BDAT");
1248 case xact_step::helo
:
1249 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush
;
1250 LOG(WARNING
) << "'BDAT' before HELO/EHLO"
1251 << (sock_
.has_peername() ? " from " : "") << client_
;
1253 case xact_step::mail
:
1254 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush
;
1255 LOG(WARNING
) << "'BDAT' before 'MAIL FROM'"
1256 << (sock_
.has_peername() ? " from " : "") << client_
;
1258 case xact_step::rcpt
:
1259 // See comment in data_start()
1260 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush
;
1261 LOG(WARNING
) << "no valid recipients"
1262 << (sock_
.has_peername() ? " from " : "") << client_
;
1264 case xact_step::data
: // first bdat
1266 case xact_step::bdat
: return true;
1267 case xact_step::rset
:
1268 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush
;
1269 LOG(WARNING
) << "error state must be cleared with a RSET"
1270 << (sock_
.has_peername() ? " from " : "") << client_
;
1274 state_
= xact_step::bdat
;
1279 void Session::bdat_done(size_t n
, bool last
)
1281 if (state_
!= xact_step::bdat
) {
1290 if (msg_
->size_error()) {
1296 out_() << "250 2.0.0 BDAT " << n
<< " OK\r\n" << std::flush
;
1297 LOG(INFO
) << "BDAT " << n
;
1301 // Check for and act on magic "wait" address.
1303 using namespace boost::xpressive
;
1305 sregex
const rex
= icase("wait-bdat-") >> (secs_
= +_d
);
1308 for (auto fp
: forward_path_
) {
1309 if (regex_match(fp
.local_part(), what
, rex
) ||
1310 regex_match(fp
.local_part(), what
, all_rex
)) {
1311 auto const str
= what
[secs_
].str();
1312 LOG(INFO
) << "waiting at BDAT " << str
<< " seconds";
1314 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
1315 google::FlushLogFiles(google::INFO
);
1316 out_() << std::flush
;
1318 LOG(INFO
) << "done waiting";
1325 out_() << "250 2.0.0 BDAT " << n
<< " LAST OK\r\n" << std::flush
;
1326 LOG(INFO
) << "BDAT " << n
<< " LAST";
1327 LOG(INFO
) << "message delivered, " << msg_
->size() << " octets, with id "
1332 void Session::bdat_size_error()
1334 out_().clear(); // clear possible eof from input side
1335 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1339 LOG(WARNING
) << "BDAT size error";
1343 void Session::bdat_seq_error()
1345 out_().clear(); // clear possible eof from input side
1346 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush
;
1350 LOG(WARNING
) << "BDAT sequence error";
1354 void Session::bdat_io_error()
1356 out_().clear(); // clear possible eof from input side
1357 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush
;
1361 LOG(WARNING
) << "BDAT I/O error";
1365 void Session::rset()
1367 out_() << "250 2.1.5 RSET OK\r\n";
1368 // No flush RFC-2920 section 3.1, this could be part of a command group.
1369 LOG(INFO
) << "RSET";
1373 void Session::noop(std::string_view str
)
1375 last_in_group_("NOOP");
1376 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush
;
1377 LOG(INFO
) << "NOOP" << (str
.length() ? " " : "") << str
;
1380 void Session::vrfy(std::string_view str
)
1382 last_in_group_("VRFY");
1383 out_() << "252 2.1.5 try it\r\n" << std::flush
;
1384 LOG(INFO
) << "VRFY" << (str
.length() ? " " : "") << str
;
1387 void Session::help(std::string_view str
)
1389 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n" << std::flush
;
1390 LOG(INFO
) << "HELP" << (str
.length() ? " " : "") << str
;
1393 void Session::quit()
1396 // last_in_group_("QUIT");
1397 out_() << "221 2.0.0 closing connection\r\n" << std::flush
;
1398 LOG(INFO
) << "QUIT";
1402 void Session::auth()
1404 out_() << "454 4.7.0 authentication failure\r\n" << std::flush
;
1405 LOG(INFO
) << "AUTH";
1409 void Session::error(std::string_view log_msg
)
1411 out_() << "421 4.3.5 system error: " << log_msg
<< "\r\n" << std::flush
;
1412 LOG(WARNING
) << log_msg
;
1415 void Session::cmd_unrecognized(std::string_view cmd
)
1417 auto const escaped
{esc(cmd
)};
1418 LOG(WARNING
) << "command unrecognized: \"" << escaped
<< "\"";
1420 if (++n_unrecognized_cmds_
>= Config::max_unrecognized_cmds
) {
1421 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1422 << "\" exceeds limit\r\n"
1424 LOG(WARNING
) << n_unrecognized_cmds_
1425 << " unrecognized commands is too many";
1429 out_() << "500 5.5.1 command unrecognized: \"" << escaped
<< "\"\r\n"
1433 void Session::bare_lf()
1435 // Error code used by Office 365.
1436 out_() << "554 5.6.11 bare LF\r\n" << std::flush
;
1437 LOG(WARNING
) << "bare LF";
1441 void Session::max_out()
1443 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1444 LOG(WARNING
) << "message size maxed out";
1448 void Session::time_out()
1450 out_() << "421 4.4.2 time-out\r\n" << std::flush
;
1451 LOG(WARNING
) << "time-out" << (sock_
.has_peername() ? " from " : "")
1456 void Session::starttls()
1458 last_in_group_("STARTTLS");
1460 out_() << "554 5.5.1 TLS already active\r\n" << std::flush
;
1461 LOG(WARNING
) << "STARTTLS issued with TLS already active";
1463 else if (!extensions_
) {
1464 out_() << "554 5.5.1 TLS not avaliable without using EHLO\r\n"
1466 LOG(WARNING
) << "STARTTLS issued without using EHLO";
1469 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush
;
1470 if (sock_
.starttls_server(config_path_
)) {
1472 max_msg_size(Config::max_msg_size_bro
);
1473 LOG(INFO
) << "STARTTLS " << sock_
.tls_info();
1476 LOG(INFO
) << "failed STARTTLS";
1481 void Session::exit_()
1483 // sock_.log_totals();
1485 timespec time_used
{};
1486 clock_gettime(CLOCK_PROCESS_CPUTIME_ID
, &time_used
);
1488 LOG(INFO
) << "CPU time " << time_used
.tv_sec
<< "." << std::setw(9)
1489 << std::setfill('0') << time_used
.tv_nsec
<< " seconds";
1491 std::exit(EXIT_SUCCESS
);
1494 /////////////////////////////////////////////////////////////////////////////
1496 // All of the verify_* functions send their own error messages back to
1497 // the client on failure, and return false.
1499 bool Session::verify_ip_address_(std::string
& error_msg
)
1501 auto ip_block_db_name
= config_path_
/ "ip-block";
1503 if (ip_block
.open(ip_block_db_name
) &&
1504 ip_block
.contains(sock_
.them_c_str())) {
1506 fmt::format("IP address {} on static blocklist", sock_
.them_c_str());
1507 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1511 client_fcrdns_
.clear();
1513 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1514 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1515 LOG(INFO
) << "loopback address allowed";
1517 client_fcrdns_
.emplace_back("localhost");
1518 client_
= fmt::format("localhost {}", sock_
.them_address_literal());
1522 if (IP::is_private(sock_
.them_address_literal())) {
1523 LOG(INFO
) << "private address allowed";
1525 client_
= sock_
.them_address_literal();
1529 auto const fcrdns
= DNS::fcrdns(res_
, sock_
.them_c_str());
1530 for (auto const& fcr
: fcrdns
) {
1531 client_fcrdns_
.emplace_back(fcr
);
1534 if (!client_fcrdns_
.empty()) {
1535 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1536 sock_
.them_address_literal());
1538 for (auto const& client_fcrdns
: client_fcrdns_
) {
1539 if (allow_
.contains(client_fcrdns
.ascii())) {
1540 LOG(INFO
) << "FCrDNS " << client_fcrdns
<< " allowed";
1541 fcrdns_allowed_
= true;
1544 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1546 if (allow_
.contains(tld
)) {
1547 LOG(INFO
) << "FCrDNS registered domain " << tld
<< " allowed";
1548 fcrdns_allowed_
= true;
1554 for (auto const& client_fcrdns
: client_fcrdns_
) {
1555 if (block_
.contains(client_fcrdns
.ascii())) {
1557 fmt::format("FCrDNS {} on static blocklist", client_fcrdns
.ascii());
1558 out_() << "554 5.7.1 blocklisted\r\n" << std::flush
;
1562 auto const tld
{tld_db_
.get_registered_domain(client_fcrdns
.ascii())};
1564 if (block_
.contains(tld
)) {
1565 error_msg
= fmt::format(
1566 "FCrDNS registered domain {} on static blocklist", tld
);
1567 out_() << "554 5.7.1 blocklisted\r\n" << std::flush
;
1574 client_
= fmt::format("{}", sock_
.them_address_literal());
1577 if (IP4::is_address(sock_
.them_c_str())) {
1579 auto const reversed
{IP4::reverse(sock_
.them_c_str())};
1582 // Check with allow list.
1583 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1586 for (auto wl : Config::wls) {
1587 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1588 if (q.has_record()) {
1589 using namespace boost::xpressive;
1591 auto const as = q.get_strings()[0];
1592 LOG(INFO) << "on allow list " << wl << " as " << as;
1596 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1599 if (regex_match(as, what, rex)) {
1600 auto const x = what[x_].str();
1601 auto const y = what[y_].str();
1604 std::from_chars(y.data(), y.data() + y.size(), value);
1607 LOG(INFO) << "allowed";
1611 LOG(INFO) << "Any A record skips check on block list";
1617 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1618 std::shuffle(std::begin(Config::bls
), std::end(Config::bls
),
1621 for (auto bl
: Config::bls
) {
1623 DNS::Query
q(res_
, DNS::RR_type::A
, reversed
+ bl
);
1624 if (q
.has_record()) {
1625 auto const as
= q
.get_strings()[0];
1626 if (as
== "127.0.1.1") {
1627 LOG(INFO
) << "Query blocked by " << bl
;
1629 else if (as
== "127.255.255.252") {
1630 LOG(INFO
) << "Typing error in DNSBL name " << bl
;
1632 else if (as
== "127.255.255.254") {
1633 LOG(INFO
) << "Anonymous query through public resolver " << bl
;
1635 else if (as
== "127.255.255.255") {
1636 LOG(INFO
) << "Excessive number of queries " << bl
;
1639 error_msg
= fmt::format("IP address {} blocked: {} returned {}",
1640 sock_
.them_c_str(), bl
, as
);
1641 out_() << "554 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1646 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1649 LOG(INFO
) << "IP address okay";
1653 // check the identity from HELO/EHLO
1654 bool Session::verify_client_(Domain
const& client_identity
,
1655 std::string
& error_msg
)
1657 if (!client_fcrdns_
.empty()) {
1658 if (auto id
= std::find(begin(client_fcrdns_
), end(client_fcrdns_
),
1660 id
!= end(client_fcrdns_
)) {
1661 // If the HELO ident is one of the FCrDNS names...
1662 if (id
!= begin(client_fcrdns_
)) {
1663 // ...then rotate that one to the front of the list
1664 std::rotate(begin(client_fcrdns_
), id
, id
+ 1);
1666 client_
= fmt::format("{} {}", client_fcrdns_
.front().ascii(),
1667 sock_
.them_address_literal());
1670 LOG(INFO
) << "claimed identity " << client_identity
1671 << " does NOT match any FCrDNS: ";
1672 for (auto const& client_fcrdns
: client_fcrdns_
) {
1673 LOG(INFO
) << " " << client_fcrdns
;
1677 // Bogus clients claim to be us or some local host.
1678 if (sock_
.has_peername() && ((client_identity
== server_identity_
) ||
1679 (client_identity
== "localhost") ||
1680 (client_identity
== "localhost.localdomain"))) {
1682 if ((sock_
.them_address_literal() == IP4::loopback_literal
) ||
1683 (sock_
.them_address_literal() == IP6::loopback_literal
)) {
1689 LOG(INFO
) << "allow-listed IP address can claim to be "
1694 // Ease up in test mode.
1695 if (FLAGS_test_mode
|| getenv("GHSMTP_TEST_MODE")) {
1699 error_msg
= fmt::format("liar, claimed to be {}", client_identity
.ascii());
1700 out_() << "550 5.7.1 liar\r\n" << std::flush
;
1704 std::vector
<std::string
> labels
;
1705 boost::algorithm::split(labels
, client_identity
.ascii(),
1706 boost::algorithm::is_any_of("."));
1707 if (labels
.size() < 2) {
1709 fmt::format("claimed bogus identity {}", client_identity
.ascii());
1710 out_() << "550 4.7.1 bogus identity\r\n" << std::flush
;
1712 // // Sometimes we may want to look at mail from non conforming
1713 // // sending systems.
1714 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1715 // << client_ << " claiming " << client_identity;
1719 if (lookup_domain(block_
, client_identity
)) {
1721 fmt::format("claimed blocked identity {}", client_identity
.ascii());
1722 out_() << "550 4.7.1 blocked identity\r\n" << std::flush
;
1726 auto const tld
{tld_db_
.get_registered_domain(client_identity
.ascii())};
1728 // Sometimes we may want to look at mail from misconfigured
1730 // LOG(WARNING) << "claimed identity has no registered domain";
1733 else if (block_
.contains(tld
)) {
1735 fmt::format("claimed identity has blocked registered domain {}", tld
);
1736 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush
;
1740 // not otherwise objectionable
1744 // check sender from RFC5321 MAIL FROM:
1745 bool Session::verify_sender_(Mailbox
const& sender
, std::string
& error_msg
)
1747 std::string
const sender_str
{sender
};
1749 auto bad_senders_db_name
= config_path_
/ "bad_senders";
1751 if (bad_senders
.open(bad_senders_db_name
) &&
1752 bad_senders
.contains(sender_str
)) {
1753 error_msg
= fmt::format("{} bad sender", sender_str
);
1754 out_() << "550 5.1.8 " << error_msg
<< "\r\n" << std::flush
;
1758 // We don't accept mail /from/ a domain we are expecting to accept
1759 // mail for on an external network connection.
1761 if (sock_
.them_address_literal() != sock_
.us_address_literal()) {
1762 if ((accept_domains_
.is_open() &&
1763 (accept_domains_
.contains(sender
.domain().ascii()) ||
1764 accept_domains_
.contains(sender
.domain().utf8()))) ||
1765 (sender
.domain() == server_identity_
)) {
1767 // Ease up in test mode.
1768 if (FLAGS_test_mode
|| getenv("GHSMTP_TEST_MODE")) {
1771 out_() << "550 5.7.1 liar\r\n" << std::flush
;
1772 error_msg
= fmt::format("liar, claimed to be {}", sender
.domain());
1777 if (sender
.domain().is_address_literal()) {
1778 if (sender
.domain() != sock_
.them_address_literal()) {
1779 LOG(WARNING
) << "sender domain " << sender
.domain() << " does not match "
1780 << sock_
.them_address_literal();
1785 do_spf_check_(sender
);
1787 if (!verify_sender_domain_(sender
.domain(), error_msg
)) {
1794 // this sender is the RFC5321 MAIL FROM: domain part
1795 bool Session::verify_sender_domain_(Domain
const& sender
,
1796 std::string
& error_msg
)
1798 if (sender
.empty()) {
1800 // is used to send bounce messages.
1804 // Break sender domain into labels:
1806 std::vector
<std::string
> labels
;
1807 boost::algorithm::split(labels
, sender
.ascii(),
1808 boost::algorithm::is_any_of("."));
1810 if (labels
.size() < 2) { // This is not a valid domain.
1811 error_msg
= fmt::format("{} invalid syntax", sender
.ascii());
1812 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1816 if (lookup_domain(block_
, sender
)) {
1818 fmt::format("SPF sender domain ({}) is blocked", spf_sender_domain_
);
1819 out_() << "550 5.7.1 " << error_msg
<< "\r\n" << std::flush
;
1823 if (spf_result_
== SPF::Result::PASS
) {
1824 if (allow_
.contains(spf_sender_domain_
.ascii())) {
1825 LOG(INFO
) << "sender " << spf_sender_domain_
.ascii() << " allowed";
1830 tld_db_
.get_registered_domain(spf_sender_domain_
.ascii())};
1832 if (allow_
.contains(reg_dom
)) {
1833 LOG(INFO
) << "sender registered domain \"" << reg_dom
<< "\" allowed";
1839 LOG(INFO
) << "sender \"" << sender
<< "\" not disallowed";
1843 void Session::do_spf_check_(Mailbox
const& sender
)
1845 if (!sock_
.has_peername()) {
1846 auto const ip_addr
= "127.0.0.1"; // use localhost for local socket
1848 fmt::format("Received-SPF: pass ({}: allow-listed) client-ip={}; "
1849 "envelope-from={}; helo={};",
1850 server_id_(), ip_addr
, sender
, client_identity_
);
1851 spf_sender_domain_
= "localhost";
1855 auto const spf_srv
= SPF::Server
{server_id_().c_str()};
1856 auto spf_request
= SPF::Request
{spf_srv
};
1858 if (IP4::is_address(sock_
.them_c_str())) {
1859 spf_request
.set_ipv4_str(sock_
.them_c_str());
1861 else if (IP6::is_address(sock_
.them_c_str())) {
1862 spf_request
.set_ipv6_str(sock_
.them_c_str());
1865 LOG(FATAL
) << "bogus address " << sock_
.them_address_literal() << ", "
1866 << sock_
.them_c_str();
1869 auto const from
{static_cast<std::string
>(sender
)};
1871 spf_request
.set_env_from(from
.c_str());
1872 spf_request
.set_helo_dom(client_identity_
.ascii().c_str());
1874 auto const spf_res
{SPF::Response
{spf_request
}};
1875 spf_result_
= spf_res
.result();
1876 spf_received_
= spf_res
.received_spf();
1877 spf_sender_domain_
= spf_request
.get_sender_dom();
1879 LOG(INFO
) << "spf_received_ == " << spf_received_
;
1881 if (spf_result_
== SPF::Result::FAIL
) {
1882 LOG(INFO
) << "FAIL " << spf_res
.header_comment();
1884 else if (spf_result_
== SPF::Result::NEUTRAL
) {
1885 LOG(INFO
) << "NEUTRAL " << spf_res
.header_comment();
1887 else if (spf_result_
== SPF::Result::PASS
) {
1888 LOG(INFO
) << "PASS " << spf_res
.header_comment();
1891 LOG(INFO
) << "INVALID/SOFTFAIL/NONE/xERROR " << spf_res
.header_comment();
1895 bool Session::verify_from_params_(parameters_t
const& parameters
)
1897 // Take a look at the optional parameters:
1898 for (auto const& [name
, value
] : parameters
) {
1899 if (iequal(name
, "BODY")) {
1900 if (iequal(value
, "8BITMIME")) {
1901 // everything is cool, this is our default...
1903 else if (iequal(value
, "7BIT")) {
1904 // nothing to see here, move along...
1906 else if (iequal(value
, "BINARYMIME")) {
1910 LOG(WARNING
) << "unrecognized BODY type \"" << value
<< "\" requested";
1913 else if (iequal(name
, "SMTPUTF8")) {
1914 if (!value
.empty()) {
1915 LOG(WARNING
) << "SMTPUTF8 parameter has a value: " << value
;
1920 // else if (iequal(name, "PRDR")) {
1921 // LOG(INFO) << "using PRDR";
1925 else if (iequal(name
, "SIZE")) {
1926 if (value
.empty()) {
1927 LOG(WARNING
) << "SIZE parameter has no value.";
1931 auto const sz
= stoull(value
);
1932 if (sz
> max_msg_size()) {
1933 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush
;
1934 LOG(WARNING
) << "SIZE parameter too large: " << sz
;
1938 catch (std::invalid_argument
const& e
) {
1939 LOG(WARNING
) << "SIZE parameter has invalid value: " << value
;
1941 catch (std::out_of_range
const& e
) {
1942 LOG(WARNING
) << "SIZE parameter has out-of-range value: " << value
;
1944 // I guess we just ignore bad size parameters.
1947 else if (iequal(name
, "REQUIRETLS")) {
1949 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush
;
1950 LOG(WARNING
) << "REQUIRETLS needed";
1955 LOG(WARNING
) << "unrecognized 'MAIL FROM' parameter " << name
<< "="
1963 bool Session::verify_rcpt_params_(parameters_t
const& parameters
)
1965 // Take a look at the optional parameters:
1966 for (auto const& [name
, value
] : parameters
) {
1967 if (iequal(name
, "RRVS")) {
1968 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1969 LOG(INFO
) << name
<< "=" << value
;
1972 LOG(WARNING
) << "unrecognized 'RCPT TO' parameter " << name
<< "="
1980 // check recipient from RFC5321 RCPT TO:
1981 bool Session::verify_recipient_(Mailbox
const& recipient
)
1983 if ((recipient
.local_part() == "Postmaster") && (recipient
.domain() == "")) {
1984 LOG(INFO
) << "magic Postmaster address";
1988 auto const accepted_domain
{[this, &recipient
] {
1989 if (recipient
.domain().is_address_literal()) {
1990 if (recipient
.domain() != sock_
.us_address_literal()) {
1991 LOG(WARNING
) << "recipient.domain address " << recipient
.domain()
1992 << " does not match ours " << sock_
.us_address_literal();
1998 // Domains we accept mail for.
1999 if (accept_domains_
.is_open()) {
2000 if (accept_domains_
.contains(recipient
.domain().ascii()) ||
2001 accept_domains_
.contains(recipient
.domain().utf8())) {
2006 // If we have no list of domains to accept, at least take our own.
2007 if (recipient
.domain() == server_id_()) {
2015 if (!accepted_domain
) {
2016 out_() << "554 5.7.1 relay access denied\r\n" << std::flush
;
2017 LOG(WARNING
) << "relay access denied for domain " << recipient
.domain();
2021 // Check for local addresses we reject.
2023 auto bad_recipients_db_name
= config_path_
/ "bad_recipients";
2024 CDB bad_recipients_db
;
2025 if (bad_recipients_db
.open(bad_recipients_db_name
) &&
2026 bad_recipients_db
.contains(recipient
.local_part())) {
2027 out_() << "550 5.1.1 bad recipient " << recipient
<< "\r\n" << std::flush
;
2028 LOG(WARNING
) << "bad recipient " << recipient
;
2034 auto fail_db_name
= config_path_
/ "fail_554";
2035 if (fs::exists(fail_db_name
)) {
2037 if (fail_db
.open(fail_db_name
) &&
2038 fail_db
.contains(recipient
.local_part())) {
2039 out_() << "554 5.7.1 prohibited for policy reasons" << recipient
2042 LOG(WARNING
) << "fail_554 recipient " << recipient
;
2049 auto temp_fail_db_name
= config_path_
/ "temp_fail";
2051 if (temp_fail
.open(temp_fail_db_name
) &&
2052 temp_fail
.contains(recipient
.local_part())) {
2053 out_() << "432 4.3.0 recipient's incoming mail queue has been stopped\r\n"
2055 LOG(WARNING
) << "temp fail for recipient " << recipient
;
2060 // Check for and act on magic "wait" address.
2062 using namespace boost::xpressive
;
2064 sregex
const rex
= icase("wait-rcpt-") >> (secs_
= +_d
);
2067 if (regex_match(recipient
.local_part(), what
, rex
) ||
2068 regex_match(recipient
.local_part(), what
, all_rex
)) {
2069 auto const str
= what
[secs_
].str();
2070 LOG(INFO
) << "waiting at RCPT TO " << str
<< " seconds";
2072 std::from_chars(str
.data(), str
.data() + str
.size(), value
);
2073 google::FlushLogFiles(google::INFO
);
2074 out_() << std::flush
;
2076 LOG(INFO
) << "done waiting";
2080 // This is a trap for a probe done by some senders to see if we
2081 // accept just any old local-part.
2083 if (recipient
.local_part().length() > 8) {
2084 out_() << "550 5.1.1 unknown recipient " << recipient
<< "\r\n"
2086 LOG(WARNING
) << "unknown recipient for HELO " << recipient
;