two SPF checks, one for helo and one for MailFrom
[ghsmtp.git] / Session.cpp
blobce4c5edd7bba9bf0970820e640ab21999d617f22
1 #include <algorithm>
2 #include <charconv>
3 #include <iomanip>
4 #include <iostream>
5 #include <string>
6 #include <vector>
8 #include "DNS.hpp"
9 #include "Domain.hpp"
10 #include "IP.hpp"
11 #include "IP4.hpp"
12 #include "IP6.hpp"
13 #include "MessageStore.hpp"
14 #include "Session.hpp"
15 #include "esc.hpp"
16 #include "iequal.hpp"
17 #include "osutil.hpp"
19 #include <fmt/format.h>
20 #include <fmt/ostream.h>
22 #include <boost/algorithm/string/classification.hpp>
23 #include <boost/algorithm/string/split.hpp>
25 #include <boost/xpressive/xpressive.hpp>
27 #include <syslog.h>
29 DEFINE_string(selector, "ghsmtp", "DKIM selector");
31 using namespace std::string_literals;
33 namespace Config {
34 char const* wls[]{
35 "list.dnswl.org",
39 <https://www.dnswl.org/?page_id=15#query>
41 Return codes
43 The return codes are structured as 127.0.x.y, with “x” indicating the category
44 of an entry and “y” indicating how trustworthy an entry has been judged.
46 Categories (127.0.X.y):
48 2 – Financial services
49 3 – Email Service Providers
50 4 – Organisations (both for-profit [ie companies] and non-profit)
51 5 – Service/network providers
52 6 – Personal/private servers
53 7 – Travel/leisure industry
54 8 – Public sector/governments
55 9 – Media and Tech companies
56 10 – some special cases
57 11 – Education, academic
58 12 – Healthcare
59 13 – Manufacturing/Industrial
60 14 – Retail/Wholesale/Services
61 15 – Email Marketing Providers
62 20 – Added through Self Service without specific category
64 Trustworthiness / Score (127.0.x.Y):
66 0 = none – only avoid outright blocking (eg large ESP mailservers, -0.1)
67 1 = low – reduce chance of false positives (-1.0)
68 2 = medium – make sure to avoid false positives but allow override for clear
69 cases (-10.0) 3 = high – avoid override (-100.0).
71 The scores in parantheses are typical SpamAssassin scores.
73 Special return code 127.0.0.255
75 In cases where your nameserver issues more than 100’000 queries / 24 hours, you
76 may be blocked from further queries. The return code “127.0.0.255” indicates
77 this situation.
81 char const* bls[]{
82 "b.barracudacentral.org",
83 "psbl.surriel.com",
84 "zen.spamhaus.org",
87 /*** Last octet from A record returned by blocklists ***
89 From <http://uribl.com/about.shtml#implementation>
91 X Binary On List
92 ---------------------------------------------------------
93 1 00000001 Query blocked, possibly due to high volume
94 2 00000010 black
95 4 00000100 grey
96 8 00001000 red
97 14 00001110 black,grey,red (for testpoints)
99 From <https://www.spamhaus.org/faq/section/Spamhaus%20DBL#291>
101 Return Codes Data Source
102 127.0.1.2 spam domain
103 127.0.1.4 phish domain
104 127.0.1.5 malware domain
105 127.0.1.6 botnet C&C domain
106 127.0.1.102 abused legit spam
107 127.0.1.103 abused spammed redirector domain
108 127.0.1.104 abused legit phish
109 127.0.1.105 abused legit malware
110 127.0.1.106 abused legit botnet C&C
111 127.0.1.255 IP queries prohibited!
113 From <http://www.surbl.org/lists#multi>
115 last octet indicates which lists it belongs to. The bit positions in
116 that last octet for membership in the different lists are:
118 8 = listed on PH
119 16 = listed on MW
120 64 = listed on ABUSE
121 128 = listed on CR
125 char const* uribls[]{
126 "multi.uribl.com",
127 "dbl.spamhaus.org",
128 "multi.surbl.org",
131 constexpr auto greeting_wait = std::chrono::seconds{2};
132 constexpr int max_recipients_per_message = 100;
133 constexpr int max_unrecognized_cmds = 20;
135 // Read timeout value gleaned from RFC-1123 section 5.3.2 and RFC-5321
136 // section 4.5.3.2.7.
137 constexpr auto read_timeout = std::chrono::minutes{5};
138 constexpr auto write_timeout = std::chrono::seconds{30};
139 } // namespace Config
141 #include <gflags/gflags.h>
143 DEFINE_bool(test_mode, false, "ease up on some checks");
145 DEFINE_bool(immortal, false, "don't set process timout");
147 DEFINE_uint64(max_read, 0, "max data to read");
148 DEFINE_uint64(max_write, 0, "max data to write");
150 DEFINE_bool(rrvs, false, "support RRVS à la RFC 7293");
152 Session::Session(fs::path config_path,
153 std::function<void(void)> read_hook,
154 int fd_in,
155 int fd_out)
156 : config_path_(config_path)
157 , res_(config_path)
158 , sock_(fd_in, fd_out, read_hook, Config::read_timeout, Config::write_timeout)
159 , send_(config_path, "smtp")
160 , srs_(config_path)
162 auto accept_db_name = config_path_ / "accept_domains";
163 auto allow_db_name = config_path_ / "allow";
164 auto block_db_name = config_path_ / "block";
165 auto forward_db_name = config_path_ / "forward";
167 accept_domains_.open(accept_db_name);
168 allow_.open(allow_db_name);
169 block_.open(block_db_name);
170 forward_.open(forward_db_name);
172 if (sock_.has_peername() && !IP::is_private(sock_.us_c_str())) {
173 auto fcrdns = DNS::fcrdns(res_, sock_.us_c_str());
174 for (auto const& fcr : fcrdns) {
175 server_fcrdns_.emplace_back(fcr);
179 server_identity_ = [this] {
180 auto const id_from_env{getenv("GHSMTP_SERVER_ID")};
181 if (id_from_env)
182 return std::string{id_from_env};
184 auto const hostname{osutil::get_hostname()};
185 if (hostname.find('.') != std::string::npos)
186 return hostname;
188 if (!server_fcrdns_.empty()) {
189 // first result should be shortest
190 return server_fcrdns_.front().ascii();
193 auto const us_c_str = sock_.us_c_str();
194 if (us_c_str && !IP::is_private(us_c_str)) {
195 return IP::to_address_literal(us_c_str);
198 LOG(FATAL) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
199 return ""s;
200 }();
202 send_.set_sender(server_identity_);
204 max_msg_size(Config::max_msg_size_initial);
207 void Session::max_msg_size(size_t max)
209 max_msg_size_ = max; // number to advertise via RFC 1870
211 if (FLAGS_max_read) {
212 sock_.set_max_read(FLAGS_max_read);
214 else {
215 auto const overhead = std::max(max / 10, size_t(2048));
216 sock_.set_max_read(max + overhead);
220 void Session::bad_host_(char const* msg) const
222 if (sock_.has_peername()) {
223 // On my systems, this pattern triggers a fail2ban rule that
224 // blocks connections from this IP address on port 25 for a few
225 // days. See <https://www.fail2ban.org/> for more info.
226 syslog(LOG_MAIL | LOG_WARNING, "bad host [%s] %s", sock_.them_c_str(), msg);
228 std::exit(EXIT_SUCCESS);
231 void Session::reset_()
233 // RSET does not force another EHLO/HELO, the one piece of per
234 // transaction data saved is client_identity_:
236 // client_identity_.clear(); <-- not cleared!
238 reverse_path_.clear();
239 forward_path_.clear();
240 spf_received_helo_.clear();
241 spf_received_mailfrom_.clear();
242 fwd_path_.clear();
243 fwd_from_.clear();
244 rep_info_.clear();
246 binarymime_ = false;
247 smtputf8_ = false;
248 // prdr_ = false;
250 if (msg_) {
251 msg_.reset();
254 max_msg_size(max_msg_size());
256 state_ = xact_step::mail;
257 send_.rset();
260 // Return codes from connection establishment are 220 or 554, according
261 // to RFC 5321. That's it.
263 void Session::greeting()
265 CHECK(state_ == xact_step::helo);
267 if (sock_.has_peername()) {
268 close(2); // if we're a networked program, never send to stderr
270 std::string error_msg;
271 if (!verify_ip_address_(error_msg)) {
272 // no glog message at this point
273 bad_host_(error_msg.c_str());
276 /******************************************************************
277 <https://tools.ietf.org/html/rfc5321#section-4.3.1> says:
279 4.3. Sequencing of Commands and Replies
281 4.3.1. Sequencing Overview
283 The communication between the sender and receiver is an alternating
284 dialogue, controlled by the sender. As such, the sender issues a
285 command and the receiver responds with a reply. Unless other
286 arrangements are negotiated through service extensions, the sender
287 MUST wait for this response before sending further commands. One
288 important reply is the connection greeting. Normally, a receiver
289 will send a 220 "Service ready" reply when the connection is
290 completed. The sender SHOULD wait for this greeting message before
291 sending any commands.
293 So which is it?
295 “…the receiver responds with a reply.”
296 “…the sender MUST wait for this response…”
297 “One important reply is the connection greeting.”
298 “The sender SHOULD wait for this greeting…”
300 So is it MUST or SHOULD? I enforce MUST.
301 *******************************************************************/
303 // Wait a bit of time for pre-greeting traffic.
304 if (!(ip_allowed_ || fcrdns_allowed_)) {
305 if (sock_.input_ready(Config::greeting_wait)) {
306 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
307 // no glog message at this point
308 bad_host_("input before any greeting");
310 // Give a half greeting and wait again.
311 out_() << "220-" << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
312 if (sock_.input_ready(Config::greeting_wait)) {
313 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
314 // LOG(INFO) << "half greeting got " << client_;
315 bad_host_("input before full greeting");
318 LOG(INFO) << "connect from " << client_;
321 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
323 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr)) {
324 alarm(2 * 60); // initial alarm
328 void Session::flush() { out_() << std::flush; }
330 void Session::last_in_group_(std::string_view verb)
332 if (sock_.input_ready(std::chrono::seconds(0))) {
333 LOG(WARNING) << "pipelining error; input ready processing " << verb;
337 void Session::lo_(char const* verb, std::string_view client_identity)
339 last_in_group_(verb);
340 reset_();
342 if (client_identity_ != client_identity) {
343 client_identity_ = client_identity;
345 std::string error_msg;
346 if (!verify_client_(client_identity_, error_msg)) {
347 // no glog message at this point
348 bad_host_(error_msg.c_str());
352 if (*verb == 'H') {
353 out_() << "250 " << server_id_() << "\r\n";
356 if (*verb == 'E') {
357 extensions_ = true;
359 if (sock_.has_peername()) {
360 out_() << "250-" << server_id_() << " at your service, " << client_
361 << "\r\n";
363 else {
364 out_() << "250-" << server_id_() << "\r\n";
367 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
368 out_() << "250-8BITMIME\r\n"; // RFC 6152
370 if (FLAGS_rrvs) {
371 out_() << "250-RRVS\r\n"; // RFC 7293
374 // out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
376 if (sock_.tls()) {
377 // Check sasl sources for auth types.
378 // out_() << "250-AUTH PLAIN\r\n";
379 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
381 else {
382 // If we're not already TLS, offer TLS
383 out_() << "250-STARTTLS\r\n"; // RFC 3207
385 out_() << "250-ENHANCEDSTATUSCODES\r\n" // RFC 2034
386 "250-PIPELINING\r\n" // RFC 2920
387 //-----------------------------------------------
388 // Disable this right now, nobody uses it anyhow,
389 // but this might break DKIM signing for relay.
390 // "250-BINARYMIME\r\n" // RFC 3030
391 //-----------------------------------------------
392 "250-CHUNKING\r\n" // RFC 3030
393 "250 SMTPUTF8\r\n"; // RFC 6531
396 out_() << std::flush;
398 if (sock_.has_peername()) {
399 if (std::find(begin(client_fcrdns_), end(client_fcrdns_),
400 client_identity_) != end(client_fcrdns_)) {
401 LOG(INFO) << verb << " " << client_identity << " from "
402 << sock_.them_address_literal();
404 else {
405 LOG(INFO) << verb << " " << client_identity << " from " << client_;
408 else {
409 LOG(INFO) << verb << " " << client_identity;
413 void Session::mail_from(Mailbox&& reverse_path, parameters_t const& parameters)
415 switch (state_) {
416 case xact_step::helo:
417 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
418 LOG(WARNING) << "'MAIL FROM' before HELO/EHLO"
419 << (sock_.has_peername() ? " from " : "") << client_;
420 return;
421 case xact_step::mail: break;
422 case xact_step::rcpt:
423 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
424 LOG(WARNING) << "nested MAIL command"
425 << (sock_.has_peername() ? " from " : "") << client_;
426 return;
427 case xact_step::data:
428 case xact_step::bdat:
429 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush;
430 LOG(WARNING) << "nested MAIL command"
431 << (sock_.has_peername() ? " from " : "") << client_;
432 return;
433 case xact_step::rset:
434 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
435 LOG(WARNING) << "error state must be cleared with a RSET"
436 << (sock_.has_peername() ? " from " : "") << client_;
437 return;
440 if (!verify_from_params_(parameters)) {
441 return;
444 std::string error_msg;
445 if (!verify_sender_(reverse_path, error_msg)) {
446 LOG(WARNING) << "verify sender failed: " << error_msg;
447 bad_host_(error_msg.c_str());
450 reverse_path_ = std::move(reverse_path);
451 fwd_path_.clear();
452 fwd_from_.clear();
453 forward_path_.clear();
454 out_() << "250 2.1.0 MAIL FROM OK\r\n";
455 // No flush RFC-2920 section 3.1, this could be part of a command group.
457 fmt::memory_buffer params;
458 for (auto const& [name, value] : parameters) {
459 fmt::format_to(params, " {}", name);
460 if (!value.empty()) {
461 fmt::format_to(params, "={}", value);
464 LOG(INFO) << "MAIL FROM:<" << reverse_path_ << ">" << fmt::to_string(params);
466 state_ = xact_step::rcpt;
469 bool Session::forward_to_(std::string const& forward, Mailbox const& rcpt_to)
471 // If we're already forwarding or replying, reject
472 if (!fwd_path_.empty() || !rep_info_.empty()) {
473 out_() << "432 4.3.0 Recipient's incoming mail queue has been stopped\r\n"
474 << std::flush;
475 LOG(WARNING) << "failed to forward to <" << forward
476 << "> already forwarding or replying for: " << rcpt_to;
477 return false;
480 fwd_path_ = Mailbox(forward);
481 fwd_from_ = rcpt_to;
483 // New bounce address
484 SRS0::from_to bounce;
485 bounce.mail_from = reverse_path_.as_string(Mailbox::domain_encoding::ascii);
487 auto const new_bounce = srs_.enc_bounce(bounce, server_id_().c_str());
489 auto const mail_from = Mailbox(new_bounce);
491 std::string error_msg;
492 if (!send_.mail_from_rcpt_to(res_, mail_from, fwd_path_, error_msg)) {
493 out_() << error_msg << std::flush;
494 LOG(WARNING) << "failed to forward <" << fwd_path_ << "> " << error_msg;
495 return false;
498 LOG(INFO) << "RCPT TO:<" << rcpt_to << "> forwarding to == <" << fwd_path_
499 << ">";
500 return true;
503 bool Session::reply_to_(SRS0::from_to const& reply_info, Mailbox const& rcpt_to)
505 // If we're already forwarding or replying, reject
506 if (!fwd_path_.empty() || !rep_info_.empty()) {
507 out_() << "432 4.3.0 Recipient's incoming mail queue has been stopped\r\n"
508 << std::flush;
509 LOG(WARNING) << "failed to reply to <" << reply_info.mail_from
510 << "> already forwarding or replying for: " << rcpt_to;
511 return false;
514 rep_info_ = reply_info;
516 Mailbox const from(rep_info_.rcpt_to_local_part, server_identity_);
517 Mailbox const to(rep_info_.mail_from);
519 std::string error_msg;
520 if (!send_.mail_from_rcpt_to(res_, from, to, error_msg)) {
521 out_() << error_msg << std::flush;
522 LOG(WARNING) << "failed to reply from <" << from << "> to <" << to << "> "
523 << error_msg;
524 return false;
527 LOG(INFO) << "RCPT TO:<" << rcpt_to << "> is a reply to "
528 << rep_info_.mail_from << " from " << rep_info_.rcpt_to_local_part;
529 return true;
532 void Session::rcpt_to(Mailbox&& forward_path, parameters_t const& parameters)
534 switch (state_) {
535 case xact_step::helo:
536 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
537 LOG(WARNING) << "'RCPT TO' before HELO/EHLO"
538 << (sock_.has_peername() ? " from " : "") << client_;
539 return;
540 case xact_step::mail:
541 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
542 LOG(WARNING) << "'RCPT TO' before 'MAIL FROM'"
543 << (sock_.has_peername() ? " from " : "") << client_;
544 return;
545 case xact_step::rcpt:
546 case xact_step::data: break;
547 case xact_step::bdat:
548 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
549 LOG(WARNING) << "'RCPT TO' during BDAT transfer"
550 << (sock_.has_peername() ? " from " : "") << client_;
551 return;
552 case xact_step::rset:
553 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
554 LOG(WARNING) << "error state must be cleared with a RSET"
555 << (sock_.has_peername() ? " from " : "") << client_;
556 return;
559 if (!verify_rcpt_params_(parameters))
560 return;
562 if (!verify_recipient_(forward_path))
563 return;
565 if (forward_path_.size() >= Config::max_recipients_per_message) {
566 out_() << "452 4.5.3 too many recipients\r\n" << std::flush;
567 LOG(WARNING) << "too many recipients <" << forward_path << ">";
568 return;
570 // no check for dups, postfix doesn't
571 forward_path_.emplace_back(std::move(forward_path));
573 Mailbox const& rcpt_to_mbx = forward_path_.back();
575 auto const rcpt_to_str =
576 rcpt_to_mbx.as_string(Mailbox::domain_encoding::ascii);
578 if (auto reply = srs_.dec_reply(rcpt_to_mbx.local_part()); reply) {
579 if (!reply_to_(*reply, rcpt_to_mbx))
580 return;
582 else if (auto const forward = forward_.find(rcpt_to_str.c_str()); forward) {
583 if (!forward_to_(*forward, rcpt_to_mbx))
584 return;
586 else {
587 LOG(INFO) << "RCPT TO:<" << rcpt_to_str << ">";
590 // No flush RFC-2920 section 3.1, this could be part of a command group.
591 out_() << "250 2.1.5 RCPT TO OK\r\n";
593 state_ = xact_step::data;
596 // The headers Received and Received-SPF are returned as a string.
598 std::string Session::added_headers_(MessageStore const& msg)
600 auto const protocol{[this]() {
601 if (smtputf8_)
602 return sock_.tls() ? "UTF8SMTPS" : "UTF8SMTP";
603 else if (extensions_)
604 return sock_.tls() ? "ESMTPS" : "ESMTP";
605 else
606 return sock_.tls() ? "SMTPS" : "SMTP";
607 }()};
609 fmt::memory_buffer headers;
611 // <https://tools.ietf.org/html/rfc5321#section-4.4>
612 fmt::format_to(headers, "Received: from {}", client_identity_.utf8());
613 if (sock_.has_peername()) {
614 fmt::format_to(headers, " ({})", client_);
616 fmt::format_to(headers, "\r\n\tby {} with {} id {}", server_identity_.utf8(),
617 protocol, msg.id());
618 if (forward_path_.size()) {
619 fmt::format_to(headers, "\r\n\tfor <{}>", forward_path_[0]);
620 for (auto i = 1u; i < forward_path_.size(); ++i)
621 fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
623 std::string const tls_info{sock_.tls_info()};
624 if (tls_info.length()) {
625 fmt::format_to(headers, "\r\n\t({})", tls_info);
627 fmt::format_to(headers, ";\r\n\t{}\r\n", msg.when());
629 // Received-SPF:
630 if (!spf_received_helo_.empty()) {
631 fmt::format_to(headers, "{}\r\n", spf_received_helo_);
633 if (!spf_received_mailfrom_.empty()) {
634 fmt::format_to(headers, "{}\r\n", spf_received_mailfrom_);
637 return fmt::to_string(headers);
640 namespace {
641 bool lookup_domain(CDB& cdb, Domain const& domain)
643 if (!domain.empty()) {
644 if (cdb.contains(domain.ascii())) {
645 return true;
647 if (domain.is_unicode() && cdb.contains(domain.utf8())) {
648 return true;
651 return false;
653 } // namespace
655 std::tuple<Session::SpamStatus, std::string> Session::spam_status_()
657 if (spf_result_mailfrom_ == SPF::Result::FAIL &&
658 spf_result_helo_ == SPF::Result::FAIL && !ip_allowed_)
659 return {SpamStatus::spam, "SPF failed"};
661 // These should have already been rejected by verify_client_().
662 if ((reverse_path_.domain() == "localhost.local") ||
663 (reverse_path_.domain() == "localhost"))
664 return {SpamStatus::spam, "bogus reverse_path"};
666 std::vector<std::string> why_ham;
668 // Anything enciphered tastes a lot like ham.
669 if (sock_.tls())
670 why_ham.emplace_back("they used TLS");
672 if (spf_result_helo_ == SPF::Result::PASS) {
673 if (lookup_domain(allow_, spf_sender_domain_helo_)) {
674 why_ham.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
675 spf_sender_domain_helo_.utf8()));
677 else {
678 auto tld_dom{
679 tld_db_.get_registered_domain(spf_sender_domain_helo_.ascii())};
680 if (tld_dom && allow_.contains(tld_dom)) {
681 why_ham.emplace_back(fmt::format(
682 "SPF sender registered domain ({}) is allowed", tld_dom));
687 if (spf_result_mailfrom_ == SPF::Result::PASS) {
688 if (lookup_domain(allow_, spf_sender_domain_mailfrom_)) {
689 why_ham.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
690 spf_sender_domain_mailfrom_.utf8()));
692 else {
693 auto tld_dom{
694 tld_db_.get_registered_domain(spf_sender_domain_mailfrom_.ascii())};
695 if (tld_dom && allow_.contains(tld_dom)) {
696 why_ham.emplace_back(fmt::format(
697 "SPF sender registered domain ({}) is allowed", tld_dom));
702 if (fcrdns_allowed_)
703 why_ham.emplace_back(
704 fmt::format("FCrDNS (or it's registered domain) is allowed"));
706 if (!why_ham.empty())
707 return {SpamStatus::ham,
708 fmt::format("{}", fmt::join(std::begin(why_ham), std::end(why_ham),
709 ", and "))};
711 return {SpamStatus::spam, "it's not ham"};
714 static std::string folder(Session::SpamStatus status,
715 std::vector<Mailbox> const& forward_path,
716 Mailbox const& reverse_path)
718 if (status == Session::SpamStatus::spam)
719 return ".Junk";
721 return "";
724 bool Session::msg_new()
726 CHECK((state_ == xact_step::data) || (state_ == xact_step::bdat));
728 auto const& [status, reason]{spam_status_()};
730 LOG(INFO) << ((status == SpamStatus::ham) ? "ham since " : "spam since ")
731 << reason;
733 // All sources of ham get a fresh 5 minute timeout per message.
734 if (status == SpamStatus::ham) {
735 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr))
736 alarm(5 * 60);
739 msg_ = std::make_unique<MessageStore>();
741 if (!FLAGS_max_write)
742 FLAGS_max_write = max_msg_size();
744 try {
745 msg_->open(server_id_(), FLAGS_max_write,
746 folder(status, forward_path_, reverse_path_));
747 auto const hdrs{added_headers_(*(msg_.get()))};
748 msg_->write(hdrs);
750 // fmt::memory_buffer spam_status;
751 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
752 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
753 // msg_->write(spam_status.data(), spam_status.size());
755 LOG(INFO) << "Spam-Status: "
756 << ((status == SpamStatus::spam) ? "Yes" : "No") << ", "
757 << reason;
759 return true;
761 catch (std::system_error const& e) {
762 switch (errno) {
763 case ENOSPC:
764 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
765 LOG(ERROR) << "no space";
766 msg_->trash();
767 msg_.reset();
768 return false;
770 default:
771 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
772 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
773 LOG(ERROR) << e.what();
774 msg_->trash();
775 msg_.reset();
776 return false;
779 catch (std::exception const& e) {
780 out_() << "550 5.0.0 mail error\r\n" << std::flush;
781 LOG(ERROR) << e.what();
782 msg_->trash();
783 msg_.reset();
784 return false;
787 // out_() << "550 5.0.0 mail error\r\n" << std::flush;
788 // LOG(ERROR) << "msg_new failed with no exception thrown";
789 // msg_->trash();
790 // msg_.reset();
791 // return false;
794 bool Session::msg_write(char const* s, std::streamsize count)
796 if ((state_ != xact_step::data) && (state_ != xact_step::bdat))
797 return false;
799 if (!msg_)
800 return false;
802 try {
803 if (msg_->write(s, count))
804 return true;
806 catch (std::system_error const& e) {
807 switch (errno) {
808 case ENOSPC:
809 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
810 LOG(ERROR) << "no space";
811 msg_->trash();
812 msg_.reset();
813 return false;
815 default:
816 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
817 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
818 LOG(ERROR) << e.what();
819 msg_->trash();
820 msg_.reset();
821 return false;
824 catch (std::exception const& e) {
825 out_() << "550 5.0.0 mail error\r\n" << std::flush;
826 LOG(ERROR) << e.what();
827 msg_->trash();
828 msg_.reset();
829 return false;
832 out_() << "550 5.0.0 mail error\r\n" << std::flush;
833 LOG(ERROR) << "write failed with no exception thrown";
834 msg_->trash();
835 msg_.reset();
836 return false;
839 bool Session::data_start()
841 last_in_group_("DATA");
843 switch (state_) {
844 case xact_step::helo:
845 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
846 LOG(WARNING) << "'DATA' before HELO/EHLO"
847 << (sock_.has_peername() ? " from " : "") << client_;
848 return false;
849 case xact_step::mail:
850 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
851 LOG(WARNING) << "'DATA' before 'MAIL FROM'"
852 << (sock_.has_peername() ? " from " : "") << client_;
853 return false;
854 case xact_step::rcpt:
856 /******************************************************************
857 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
859 The DATA command can fail at only two points in the protocol exchange:
861 If there was no MAIL, or no RCPT, command, or all such commands were
862 rejected, the server MAY return a "command out of sequence" (503) or
863 "no valid recipients" (554) reply in response to the DATA command.
865 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
867 The additional restriction is that when there have been no successful
868 RCPT commands in the mail transaction, the DATA command MUST fail
869 with a 503 reply code.
871 Therefore I will send the reply code that is valid for both, and
872 do the same for the BDAT case.
873 *******************************************************************/
875 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
876 LOG(WARNING) << "no valid recipients"
877 << (sock_.has_peername() ? " from " : "") << client_;
878 return false;
879 case xact_step::data: break;
880 case xact_step::bdat:
881 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
882 LOG(WARNING) << "'DATA' during BDAT transfer"
883 << (sock_.has_peername() ? " from " : "") << client_;
884 return false;
885 case xact_step::rset:
886 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
887 LOG(WARNING) << "error state must be cleared with a RSET"
888 << (sock_.has_peername() ? " from " : "") << client_;
889 return false;
892 if (binarymime_) {
893 out_() << "503 5.5.1 DATA does not support BINARYMIME\r\n" << std::flush;
894 LOG(WARNING) << "DATA does not support BINARYMIME";
895 state_ = xact_step::rset; // RFC 3030 section 3 page 5
896 return false;
899 if (!msg_new()) {
900 LOG(ERROR) << "msg_new() failed";
901 return false;
904 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush;
905 LOG(INFO) << "DATA";
906 return true;
909 bool Session::do_forward_(message::parsed& msg)
911 auto msg_fwd = msg;
913 // Generate a reply address
914 SRS0::from_to reply;
915 reply.mail_from = msg_fwd.dmarc_from;
916 reply.rcpt_to_local_part = fwd_from_.local_part();
918 auto const reply_addr =
919 fmt::format("{}@{}", srs_.enc_reply(reply), server_id_());
921 auto const munging = false;
923 auto const sender = server_identity_.ascii().c_str();
924 auto const selector = FLAGS_selector.c_str();
925 auto const key_file =
926 (config_path_ / FLAGS_selector).replace_extension("private");
927 CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
929 if (munging) {
930 auto const from_hdr =
931 fmt::format("From: \"{} via\" <@>", msg_fwd.dmarc_from, reply_addr);
932 message::rewrite_from_to(msg_fwd, from_hdr, "", sender, selector, key_file);
934 else {
935 auto const reply_to_hdr = fmt::format("Reply-To: {}", reply_addr);
936 message::rewrite_from_to(msg_fwd, "", reply_to_hdr, sender, selector,
937 key_file);
940 // Forward it on
941 if (!send_.send(msg_fwd.as_string())) {
942 out_() << "432 4.3.0 Recipient's incoming mail queue has been "
943 "stopped\r\n"
944 << std::flush;
946 LOG(ERROR) << "failed to send for " << fwd_path_;
947 return false;
950 LOG(INFO) << "successfully sent for " << fwd_path_;
951 return true;
954 bool Session::do_reply_(message::parsed& msg)
956 Mailbox to_mbx(rep_info_.mail_from);
957 Mailbox from_mbx(rep_info_.rcpt_to_local_part, server_identity_);
959 auto reply = std::make_unique<MessageStore>();
960 reply->open(server_id_(), FLAGS_max_write, ".Drafts");
962 auto const date{Now{}};
963 auto const pill{Pill{}};
964 auto const mid_str =
965 fmt::format("<{}.{}@{}>", date.sec(), pill, server_identity_);
967 fmt::memory_buffer bfr;
969 fmt::format_to(bfr, "From: <{}>\r\n", from_mbx);
970 fmt::format_to(bfr, "To: <{}>\r\n", to_mbx);
972 fmt::format_to(bfr, "Date: {}\r\n", date.c_str());
974 fmt::format_to(bfr, "Message-ID: {}\r\n", mid_str.c_str());
976 if (!msg.get_header(message::Subject).empty()) {
977 fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
978 msg.get_header(message::Subject));
980 else {
981 fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
982 "Reply to your message");
985 if (!msg.get_header(message::In_Reply_To).empty()) {
986 fmt::format_to(bfr, "{}: {}\r\n", message::In_Reply_To,
987 msg.get_header(message::In_Reply_To));
990 if (!msg.get_header(message::MIME_Version).empty() &&
991 msg.get_header(message::Content_Type).empty()) {
992 fmt::format_to(bfr, "{}: {}\r\n", message::MIME_Version,
993 msg.get_header(message::MIME_Version));
994 fmt::format_to(bfr, "{}: {}\r\n", message::Content_Type,
995 msg.get_header(message::Content_Type));
998 reply->write(fmt::to_string(bfr));
1000 if (!msg.body.empty()) {
1001 reply->write("\r\n");
1002 reply->write(msg.body);
1005 auto const msg_data = reply->freeze();
1006 message::parsed msg_reply;
1007 CHECK(msg_reply.parse(msg_data));
1009 auto const sender = server_identity_.ascii().c_str();
1010 auto const selector = FLAGS_selector.c_str();
1011 auto const key_file =
1012 (config_path_ / FLAGS_selector).replace_extension("private");
1013 CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1015 message::dkim_sign(msg_reply, sender, selector, key_file);
1017 if (!send_.send(msg_reply.as_string())) {
1018 out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1019 "stopped\r\n"
1020 << std::flush;
1022 LOG(ERROR) << "send failed for reply to " << to_mbx << " from " << from_mbx;
1023 return false;
1026 LOG(INFO) << "successful reply to " << to_mbx << " from " << from_mbx;
1027 return true;
1030 bool Session::do_deliver_()
1032 CHECK(msg_);
1034 auto const sender = server_identity_.ascii().c_str();
1035 auto const selector = FLAGS_selector.c_str();
1036 auto const key_file =
1037 (config_path_ / FLAGS_selector).replace_extension("private");
1038 CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1040 try {
1041 auto const msg_data = msg_->freeze();
1043 message::parsed msg;
1045 // Only deal in RFC-5322 Mail Objects.
1046 bool const message_parsed = msg.parse(msg_data);
1047 if (message_parsed) {
1049 // remove any Return-Path
1050 message::remove_delivery_headers(msg);
1052 auto const authentic =
1053 message_parsed &&
1054 message::authentication(msg, sender, selector, key_file);
1056 // write a new Return-Path
1057 msg_->write(fmt::format("Return-Path: <{}>\r\n", reverse_path_));
1059 for (auto const h : msg.headers) {
1060 msg_->write(h.as_string());
1061 msg_->write("\r\n");
1063 if (!msg.body.empty()) {
1064 msg_->write("\r\n");
1065 msg_->write(msg.body);
1068 msg_->deliver();
1070 if (authentic && !fwd_path_.empty()) {
1071 if (!do_forward_(msg))
1072 return false;
1074 if (authentic && !rep_info_.empty()) {
1075 if (!do_reply_(msg))
1076 return false;
1080 msg_->close();
1082 catch (std::system_error const& e) {
1083 switch (errno) {
1084 case ENOSPC:
1085 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
1086 LOG(ERROR) << "no space";
1087 msg_->trash();
1088 reset_();
1089 return false;
1091 default:
1092 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
1093 if (errno)
1094 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
1095 LOG(ERROR) << e.what();
1096 msg_->trash();
1097 reset_();
1098 return false;
1102 return true;
1105 void Session::data_done()
1107 CHECK((state_ == xact_step::data));
1109 if (msg_ && msg_->size_error()) {
1110 data_size_error();
1111 return;
1114 do_deliver_();
1116 // if (prdr_) {
1117 // out_() << "353\r\n";
1118 // for (auto fp : forward_path_) {
1119 // out_() << "250 2.1.5 RCPT TO OK\r\n";
1120 // }
1121 // }
1124 using namespace boost::xpressive;
1126 mark_tag secs_(1);
1127 sregex const rex = icase("wait-data-") >> (secs_ = +_d);
1128 smatch what;
1130 for (auto fp : forward_path_) {
1131 if (regex_match(fp.local_part(), what, rex)) {
1132 auto const str = what[secs_].str();
1133 LOG(INFO) << "waiting at DATA " << str << " seconds";
1134 long value = 0;
1135 std::from_chars(str.data(), str.data() + str.size(), value);
1136 sleep(value);
1137 LOG(INFO) << "done waiting";
1142 out_() << "250 2.0.0 DATA OK\r\n" << std::flush;
1143 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1144 << msg_->id();
1146 reset_();
1149 void Session::data_size_error()
1151 out_().clear(); // clear possible eof from input side
1152 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1153 if (msg_) {
1154 msg_->trash();
1156 LOG(WARNING) << "DATA size error";
1157 reset_();
1160 void Session::data_error()
1162 out_().clear(); // clear possible eof from input side
1163 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush;
1164 if (msg_) {
1165 msg_->trash();
1167 LOG(WARNING) << "DATA error";
1168 reset_();
1171 bool Session::bdat_start(size_t n)
1173 switch (state_) {
1174 case xact_step::helo:
1175 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
1176 LOG(WARNING) << "'BDAT' before HELO/EHLO"
1177 << (sock_.has_peername() ? " from " : "") << client_;
1178 return false;
1179 case xact_step::mail:
1180 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
1181 LOG(WARNING) << "'BDAT' before 'MAIL FROM'"
1182 << (sock_.has_peername() ? " from " : "") << client_;
1183 return false;
1184 case xact_step::rcpt:
1185 // See comment in data_start()
1186 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
1187 LOG(WARNING) << "no valid recipients"
1188 << (sock_.has_peername() ? " from " : "") << client_;
1189 return false;
1190 case xact_step::data: // first bdat
1191 break;
1192 case xact_step::bdat: return true;
1193 case xact_step::rset:
1194 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
1195 LOG(WARNING) << "error state must be cleared with a RSET"
1196 << (sock_.has_peername() ? " from " : "") << client_;
1197 return false;
1200 state_ = xact_step::bdat;
1202 return msg_new();
1205 void Session::bdat_done(size_t n, bool last)
1207 if (state_ != xact_step::bdat) {
1208 bdat_seq_error();
1209 return;
1212 if (!msg_) {
1213 return;
1216 if (msg_->size_error()) {
1217 bdat_size_error();
1218 return;
1221 if (!last) {
1222 out_() << "250 2.0.0 BDAT " << n << " OK\r\n" << std::flush;
1223 LOG(INFO) << "BDAT " << n;
1224 return;
1227 do_deliver_();
1229 out_() << "250 2.0.0 BDAT " << n << " LAST OK\r\n" << std::flush;
1231 LOG(INFO) << "BDAT " << n << " LAST";
1232 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1233 << msg_->id();
1234 reset_();
1237 void Session::bdat_size_error()
1239 out_().clear(); // clear possible eof from input side
1240 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1241 if (msg_) {
1242 msg_->trash();
1244 LOG(WARNING) << "BDAT size error";
1245 reset_();
1248 void Session::bdat_seq_error()
1250 out_().clear(); // clear possible eof from input side
1251 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush;
1252 if (msg_) {
1253 msg_->trash();
1255 LOG(WARNING) << "BDAT sequence error";
1256 reset_();
1259 void Session::bdat_io_error()
1261 out_().clear(); // clear possible eof from input side
1262 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush;
1263 if (msg_) {
1264 msg_->trash();
1266 LOG(WARNING) << "BDAT I/O error";
1267 reset_();
1270 void Session::rset()
1272 out_() << "250 2.1.5 RSET OK\r\n";
1273 // No flush RFC-2920 section 3.1, this could be part of a command group.
1274 LOG(INFO) << "RSET";
1275 reset_();
1278 void Session::noop(std::string_view str)
1280 last_in_group_("NOOP");
1281 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush;
1282 LOG(INFO) << "NOOP" << (str.length() ? " " : "") << str;
1285 void Session::vrfy(std::string_view str)
1287 last_in_group_("VRFY");
1288 out_() << "252 2.1.5 try it\r\n" << std::flush;
1289 LOG(INFO) << "VRFY" << (str.length() ? " " : "") << str;
1292 void Session::help(std::string_view str)
1294 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n" << std::flush;
1295 LOG(INFO) << "HELP" << (str.length() ? " " : "") << str;
1298 void Session::quit()
1300 send_.quit();
1301 // last_in_group_("QUIT");
1302 out_() << "221 2.0.0 closing connection\r\n" << std::flush;
1303 LOG(INFO) << "QUIT";
1304 exit_();
1307 void Session::auth()
1309 out_() << "454 4.7.0 authentication failure\r\n" << std::flush;
1310 LOG(INFO) << "AUTH";
1311 bad_host_("auth");
1314 void Session::error(std::string_view log_msg)
1316 out_() << "421 4.3.5 system error: " << log_msg << "\r\n" << std::flush;
1317 LOG(WARNING) << log_msg;
1320 void Session::cmd_unrecognized(std::string_view cmd)
1322 auto const escaped{esc(cmd)};
1323 LOG(WARNING) << "command unrecognized: \"" << escaped << "\"";
1325 if (++n_unrecognized_cmds_ >= Config::max_unrecognized_cmds) {
1326 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1327 << "\" exceeds limit\r\n"
1328 << std::flush;
1329 LOG(WARNING) << n_unrecognized_cmds_
1330 << " unrecognized commands is too many";
1331 exit_();
1334 out_() << "500 5.5.1 command unrecognized: \"" << escaped << "\"\r\n"
1335 << std::flush;
1338 void Session::bare_lf()
1340 // Error code used by Office 365.
1341 out_() << "554 5.6.11 bare LF\r\n" << std::flush;
1342 LOG(WARNING) << "bare LF";
1343 exit_();
1346 void Session::max_out()
1348 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1349 LOG(WARNING) << "message size maxed out";
1350 exit_();
1353 void Session::time_out()
1355 out_() << "421 4.4.2 time-out\r\n" << std::flush;
1356 LOG(WARNING) << "time-out" << (sock_.has_peername() ? " from " : "")
1357 << client_;
1358 exit_();
1361 void Session::starttls()
1363 last_in_group_("STARTTLS");
1364 if (sock_.tls()) {
1365 out_() << "554 5.5.1 TLS already active\r\n" << std::flush;
1366 LOG(WARNING) << "STARTTLS issued with TLS already active";
1368 else {
1369 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush;
1370 if (sock_.starttls_server(config_path_)) {
1371 reset_();
1372 max_msg_size(Config::max_msg_size_bro);
1373 LOG(INFO) << "STARTTLS " << sock_.tls_info();
1378 void Session::exit_()
1380 // sock_.log_totals();
1382 timespec time_used{};
1383 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time_used);
1385 LOG(INFO) << "CPU time " << time_used.tv_sec << "." << std::setw(9)
1386 << std::setfill('0') << time_used.tv_nsec << " seconds";
1388 std::exit(EXIT_SUCCESS);
1391 namespace {
1392 bool ip4_allowed(char const* addr)
1394 struct nw {
1395 char const* net;
1396 char const* mask;
1397 char const* comment;
1400 // clang-format off
1402 // 255 0b11111111 8
1403 // 254 0b11111110 7
1404 // 252 0b11111100 6
1405 // 248 0b11111000 5
1406 // 240 0b11110000 4
1407 // 224 0b11100000 3
1408 // 192 0b11000000 2
1409 // 128 0b10000000 1
1411 nw const networks[]{
1412 // the one very special case
1413 {"108.83.36.112", "255.255.255.248", "108.83.36.112/29"},
1415 // accept from major providers:
1416 {"3.0.0.0", "255.0.0.0", "3.0.0.0/9 and 3.128.0.0/9 Amazon"},
1417 {"5.45.198.0", "255.255.254.0", "5.45.198.0/23 YANDEX-5-45-198"},
1418 {"12.153.224.0", "255.255.255.0", "12.153.224.0/24 E-TRADE10-224"},
1419 {"17.0.0.0", "255.0.0.0", "17.0.0.0/8 APPLE-WWNET"},
1420 {"40.74.0.0", "255.254.0.0", "40.74.0.0/15 MSFT NET-40-74-0-0-1"},
1421 {"40.76.0.0", "255.252.0.0", "40.76.0.0/14 MSFT NET-40-74-0-0-1"},
1422 {"40.80.0.0", "255.240.0.0", "40.80.0.0/12 MSFT NET-40-74-0-0-1"},
1423 {"40.96.0.0", "255.240.0.0", "40.96.0.0/12 MSFT NET-40-74-0-0-1"},
1424 {"40.112.0.0", "255.248.0.0", "40.112.0.0/13 MSFT NET-40-74-0-0-1"},
1425 {"40.120.0.0", "255.252.0.0", "40.120.0.0/14 MSFT NET-40-74-0-0-1"},
1426 {"40.124.0.0", "255.255.0.0", "40.124.0.0/16 MSFT NET-40-74-0-0-1"},
1427 {"40.125.0.0", "255.255.128.0", "40.125.0.0/17 MSFT NET-40-74-0-0-1"},
1428 {"56.0.0.0", "255.0.0.0", "56.0.0.0/8 USPS1"},
1429 {"65.52.0.0", "255.252.0.0", "65.52.0.0/14 MICROSOFT-1BLK"},
1430 {"66.163.160.0", "255.255.224.0", "66.163.160.0/19 A-YAHOO-US2"},
1431 {"66.211.176.0", "255.255.240.0", "66.211.176.0/20 EBAY-2"},
1432 {"66.211.172.0", "255.255.252.0", "66.211.172.0/22 EBAY-2"},
1433 {"66.220.144.0", "255.255.240.0", "66.220.144.0/20 TFBNET3"},
1434 {"68.232.192.0", "255.255.240.0", "68.232.192.0/20 EXACT-IP-NET-2"},
1435 {"69.171.224.0", "255.255.224.0", "69.171.224.0/19 TFBNET3"},
1436 {"70.47.67.0", "255.255.255.0", "70.47.67.0/24 NET-462F4300-24"},
1437 {"74.6.0.0", "255.255.0.0", "INKTOMI-BLK-6 Oath"},
1438 {"74.125.0.0", "255.255.0.0", "74.125.0.0/16 GOOGLE"},
1439 {"75.101.100.43", "255.255.255.255", "new.toad.com"},
1440 {"76.178.68.57", "255.255.255.255", "cpe-76-178-68-57.natsow.res.rr.com"},
1441 {"98.136.0.0", "255.252.0.0", "98.136.0.0/14 A-YAHOO-US9"},
1442 {"104.40.0.0", "255.248.0.0", "104.40.0.0/13 MSFT"},
1443 {"108.174.0.0", "255.255.240.0", "108.174.0.0/20 LINKEDIN"},
1444 {"159.45.0.0", "255.255.0.0", "159.45.0.0/16 AGE-COM"},
1445 {"159.53.0.0", "255.255.0.0", "159.53.0.0/16 JMC"},
1446 {"159.135.224.0", "255.255.240.0", "159.135.224.0/20 MNO87-159-135-224-0-0"},
1447 {"162.247.72.0", "255.255.252.0", "162.247.72.0/22 CALYX-INSTITUTE-V4-1"},
1448 {"165.107.0.0", "255.255.0.0", "NET-LDC-CA-GOV"},
1449 {"192.175.128.0", "255.255.128.0", "192.175.128.0/17 NETBLK-VANGUARD"},
1450 {"198.2.128.0", "255.255.192.0", "198.2.128.0/18 RSG-DELIVERY"},
1451 {"198.252.206.0", "255.255.255.0", "198.252.206.0/24 SE-NET01"},
1452 {"199.122.120.0", "255.255.248.0", "199.122.120.0/21 EXACT-IP-NET-3"},
1453 {"204.13.164.0", "255.255.255.0", "204.13.164.0/24 RISEUP-NETWORKS-SWIFT-BLOCK2"},
1454 {"204.29.186.0", "255.255.254.0", "204.29.186.0/23 ATDN-NSCAPE"},
1455 {"205.139.104.0", "255.255.252.0", "205.139.104.0/22 SAVV-S259964-8"},
1456 {"205.201.128.0", "255.255.240.0", "205.201.128.0/20 RSG-DELIVERY"},
1457 {"208.118.235.0", "255.255.255.0", "208.118.235.0/24 TWDX-208-118-235-0-1"},
1458 {"208.192.0.0", "255.192.0.0", "208.192.0.0/10 UUNET1996B"},
1459 {"209.51.188.0", "255.255.255.0", "I:NET-209.51.188.0/24 FSF"},
1460 {"209.85.128.0", "255.255.128.0", "209.85.128.0/17 GOOGLE"},
1461 {"209.132.176.0", "255.255.240.0", "209.132.176.0/20 RED-HAT-BLK"},
1462 {"209.237.224.0", "255.255.224.0", "UNITEDLAYER-1"},
1463 // {"209.237.225.253", "255.255.255.255", "Old new.toad.com"},
1465 // clang-format on
1467 uint32_t addr32;
1468 CHECK_EQ(inet_pton(AF_INET, addr, &addr32), 1)
1469 << "can't interpret as IPv4 address";
1471 for (auto const& network : networks) {
1472 uint32_t net32;
1473 CHECK_EQ(inet_pton(AF_INET, network.net, &net32), 1)
1474 << "can't grok " << network.net;
1475 uint32_t mask32;
1476 CHECK_EQ(inet_pton(AF_INET, network.mask, &mask32), 1)
1477 << "can't grok " << network.mask;
1479 // sanity check: all unmasked bits must be zero
1480 CHECK_EQ(net32 & (~mask32), 0)
1481 << "bogus config net=" << network.net << ", mask=" << network.mask;
1483 if (net32 == (addr32 & mask32)) {
1484 LOG(INFO) << addr << " allowed " << network.comment;
1485 return true;
1489 return false;
1491 } // namespace
1493 /////////////////////////////////////////////////////////////////////////////
1495 // All of the verify_* functions send their own error messages back to
1496 // the client on failure, and return false.
1498 bool Session::verify_ip_address_(std::string& error_msg)
1500 auto ip_block_db_name = config_path_ / "ip-block";
1501 CDB ip_block{ip_block_db_name};
1502 if (ip_block.contains(sock_.them_c_str())) {
1503 error_msg =
1504 fmt::format("IP address {} on static blocklist", sock_.them_c_str());
1505 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1506 return false;
1509 client_fcrdns_.clear();
1511 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1512 (sock_.them_address_literal() == IP6::loopback_literal)) {
1513 LOG(INFO) << "loopback address allowed";
1514 ip_allowed_ = true;
1515 client_fcrdns_.emplace_back("localhost");
1516 client_ = fmt::format("localhost {}", sock_.them_address_literal());
1517 return true;
1520 if (IP::is_private(sock_.them_address_literal())) {
1521 LOG(INFO) << "local address allowed";
1522 ip_allowed_ = true;
1523 client_ = sock_.them_address_literal();
1524 return true;
1527 auto const fcrdns = DNS::fcrdns(res_, sock_.them_c_str());
1528 for (auto const& fcr : fcrdns) {
1529 client_fcrdns_.emplace_back(fcr);
1532 if (!client_fcrdns_.empty()) {
1533 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1534 sock_.them_address_literal());
1535 // check allow list
1536 for (auto const& client_fcrdns : client_fcrdns_) {
1537 if (allow_.contains(client_fcrdns.ascii())) {
1538 // LOG(INFO) << "FCrDNS " << client_fcrdns << " allowed";
1539 fcrdns_allowed_ = true;
1540 return true;
1542 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1543 if (tld) {
1544 if (allow_.contains(tld)) {
1545 // LOG(INFO) << "FCrDNS registered domain " << tld << " allowed";
1546 fcrdns_allowed_ = true;
1547 return true;
1551 // check blocklist
1552 for (auto const& client_fcrdns : client_fcrdns_) {
1553 if (block_.contains(client_fcrdns.ascii())) {
1554 error_msg =
1555 fmt::format("FCrDNS {} on static blocklist", client_fcrdns.ascii());
1556 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1557 return false;
1560 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1561 if (tld) {
1562 if (block_.contains(tld)) {
1563 error_msg = fmt::format(
1564 "FCrDNS registered domain {} on static blocklist", tld);
1565 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1566 return false;
1571 else {
1572 client_ = fmt::format("unknown {}", sock_.them_address_literal());
1575 if (IP4::is_address(sock_.them_c_str())) {
1577 if (ip4_allowed(sock_.them_c_str())) {
1578 LOG(INFO) << "on internal allow list";
1579 ip_allowed_ = true;
1580 return true;
1583 auto const reversed{IP4::reverse(sock_.them_c_str())};
1585 // Check with allow list.
1586 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1587 random_device_);
1589 for (auto wl : Config::wls) {
1590 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1591 if (q.has_record()) {
1592 using namespace boost::xpressive;
1594 auto const as = q.get_strings()[0];
1595 LOG(INFO) << "on allow list " << wl << " as " << as;
1597 mark_tag x_(1);
1598 mark_tag y_(2);
1599 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1600 smatch what;
1602 if (regex_match(as, what, rex)) {
1603 auto const x = what[x_].str();
1604 auto const y = what[y_].str();
1606 int value = 0;
1607 std::from_chars(y.data(), y.data() + y.size(), value);
1608 if (value > 0) {
1609 ip_allowed_ = true;
1610 LOG(INFO) << "allowed";
1614 // Any A record skips check on block list
1615 return true;
1619 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1620 std::shuffle(std::begin(Config::bls), std::end(Config::bls),
1621 random_device_);
1623 for (auto bl : Config::bls) {
1625 DNS::Query q(res_, DNS::RR_type::A, reversed + bl);
1626 if (q.has_record()) {
1627 auto const as = q.get_strings()[0];
1628 if (as == "127.0.1.1") {
1629 LOG(INFO) << "Query blocked by " << bl;
1631 else {
1632 error_msg = fmt::format("blocked on advice from {}", bl);
1633 LOG(INFO) << sock_.them_c_str() << " " << error_msg;
1634 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1635 return false;
1639 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1642 return true;
1645 // check the identity from HELO/EHLO
1646 bool Session::verify_client_(Domain const& client_identity,
1647 std::string& error_msg)
1649 if (!client_fcrdns_.empty()) {
1650 if (auto id = std::find(begin(client_fcrdns_), end(client_fcrdns_),
1651 client_identity);
1652 id != end(client_fcrdns_)) {
1653 if (id != begin(client_fcrdns_)) {
1654 std::rotate(begin(client_fcrdns_), id, id + 1);
1656 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1657 sock_.them_address_literal());
1658 return true;
1660 LOG(INFO) << "claimed identity " << client_identity
1661 << " does NOT match any FCrDNS: ";
1662 for (auto const& client_fcrdns : client_fcrdns_) {
1663 LOG(INFO) << " " << client_fcrdns;
1667 // Bogus clients claim to be us or some local host.
1668 if (sock_.has_peername() && ((client_identity == server_identity_) ||
1669 (client_identity == "localhost") ||
1670 (client_identity == "localhost.localdomain"))) {
1672 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1673 (sock_.them_address_literal() == IP6::loopback_literal)) {
1674 return true;
1677 // Give 'em a pass.
1678 if (ip_allowed_) {
1679 LOG(INFO) << "allow-listed IP address can claim to be "
1680 << client_identity;
1681 return true;
1684 // Ease up in test mode.
1685 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1686 return true;
1689 error_msg = fmt::format("liar, claimed to be {}", client_identity.ascii());
1690 out_() << "550 5.7.1 liar\r\n" << std::flush;
1691 return false;
1694 std::vector<std::string> labels;
1695 boost::algorithm::split(labels, client_identity.ascii(),
1696 boost::algorithm::is_any_of("."));
1697 if (labels.size() < 2) {
1698 error_msg =
1699 fmt::format("claimed bogus identity {}", client_identity.ascii());
1700 out_() << "550 4.7.1 bogus identity\r\n" << std::flush;
1701 return false;
1702 // // Sometimes we may want to look at mail from non conforming
1703 // // sending systems.
1704 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1705 // << client_ << " claiming " << client_identity;
1706 // return true;
1709 if (lookup_domain(block_, client_identity)) {
1710 error_msg =
1711 fmt::format("claimed blocked identity {}", client_identity.ascii());
1712 out_() << "550 4.7.1 blocked identity\r\n" << std::flush;
1713 return false;
1716 auto const tld{tld_db_.get_registered_domain(client_identity.ascii())};
1717 if (!tld) {
1718 // Sometimes we may want to look at mail from misconfigured
1719 // sending systems.
1720 // LOG(WARNING) << "claimed identity has no registered domain";
1721 // return true;
1723 else if (block_.contains(tld)) {
1724 error_msg =
1725 fmt::format("claimed identity has blocked registered domain {}", tld);
1726 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush;
1727 return false;
1730 // not otherwise objectionable
1731 return true;
1734 // check sender from RFC5321 MAIL FROM:
1735 bool Session::verify_sender_(Mailbox const& sender, std::string& error_msg)
1737 std::string const sender_str{sender};
1739 auto bad_senders_db_name = config_path_ / "bad_senders";
1740 CDB bad_senders{bad_senders_db_name}; // Addresses we don't accept mail from.
1741 if (bad_senders.contains(sender_str)) {
1742 error_msg = fmt::format("{} bad sender", sender_str);
1743 out_() << "501 5.1.8 " << error_msg << "\r\n" << std::flush;
1744 return false;
1747 // We don't accept mail /from/ a domain we are expecting to accept
1748 // mail for on an external network connection.
1750 if (sock_.them_address_literal() != sock_.us_address_literal()) {
1751 if ((accept_domains_.is_open() &&
1752 (accept_domains_.contains(sender.domain().ascii()) ||
1753 accept_domains_.contains(sender.domain().utf8()))) ||
1754 (sender.domain() == server_identity_)) {
1756 // Ease up in test mode.
1757 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1758 return true;
1760 out_() << "550 5.7.1 liar\r\n" << std::flush;
1761 error_msg = fmt::format("liar, claimed to be {}", sender.domain());
1762 return false;
1766 if (sender.domain().is_address_literal()) {
1767 if (sender.domain() != sock_.them_address_literal()) {
1768 LOG(WARNING) << "sender domain " << sender.domain() << " does not match "
1769 << sock_.them_address_literal();
1771 return true;
1774 if (!verify_sender_domain_(sender.domain(), error_msg)) {
1775 return false;
1778 if (!verify_sender_spf_(sender)) {
1779 error_msg = "failed SPF check";
1780 return false;
1783 return true;
1786 // this sender is the RFC5321 MAIL FROM: domain part
1787 bool Session::verify_sender_domain_(Domain const& sender,
1788 std::string& error_msg)
1790 if (sender.empty()) {
1791 // MAIL FROM:<>
1792 // is used to send bounce messages.
1793 return true;
1796 // Break sender domain into labels:
1798 std::vector<std::string> labels;
1799 boost::algorithm::split(labels, sender.ascii(),
1800 boost::algorithm::is_any_of("."));
1802 if (labels.size() < 2) { // This is not a valid domain.
1803 error_msg = fmt::format("{} invalid syntax", sender.ascii());
1804 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1805 return false;
1808 if (allow_.contains(sender.ascii())) {
1809 LOG(INFO) << "sender " << sender.ascii() << " allowed";
1810 return true;
1812 auto const reg_dom{tld_db_.get_registered_domain(sender.ascii())};
1813 if (reg_dom) {
1814 if (allow_.contains(reg_dom)) {
1815 LOG(INFO) << "sender registered domain \"" << reg_dom << "\" allowed";
1816 return true;
1819 // LOG(INFO) << "looking up " << reg_dom;
1820 return verify_sender_domain_uribl_(reg_dom, error_msg);
1823 LOG(INFO) << "sender \"" << sender << "\" not disallowed";
1824 return true;
1827 // check sender domain on dynamic URI block lists
1828 bool Session::verify_sender_domain_uribl_(std::string_view sender,
1829 std::string& error_msg)
1831 if (!sock_.has_peername()) // short circuit
1832 return true;
1834 std::shuffle(std::begin(Config::uribls), std::end(Config::uribls),
1835 random_device_);
1836 for (auto uribl : Config::uribls) {
1837 auto const lookup = fmt::format("{}.{}", sender, uribl);
1838 auto as = DNS::get_strings(res_, DNS::RR_type::A, lookup);
1839 if (!as.empty()) {
1840 if (as.front() == "127.0.0.1")
1841 continue;
1842 error_msg = fmt::format("{} blocked on advice of {}", sender, uribl);
1843 out_() << "550 5.7.1 sender " << error_msg << "\r\n" << std::flush;
1844 return false;
1848 LOG(INFO) << "sender \"" << sender << "\" not blocked by URIBLs";
1849 return true;
1852 bool Session::verify_sender_spf_(Mailbox const& sender)
1854 if (!sock_.has_peername()) {
1855 auto const ip_addr = "127.0.0.1"; // use localhost for local socket
1856 spf_received_helo_ =
1857 fmt::format("Received-SPF: pass ({}: allow-listed) client-ip={}; "
1858 "envelope-from={}; helo={};",
1859 server_id_(), ip_addr, sender, client_identity_);
1860 spf_sender_domain_helo_ = "localhost";
1861 return true;
1864 auto const spf_srv = SPF::Server{server_id_().c_str()};
1865 auto spf_request_helo = SPF::Request{spf_srv};
1866 auto spf_request_mailfrom = SPF::Request{spf_srv};
1868 if (IP4::is_address(sock_.them_c_str())) {
1869 spf_request_helo.set_ipv4_str(sock_.them_c_str());
1870 spf_request_mailfrom.set_ipv4_str(sock_.them_c_str());
1872 else if (IP6::is_address(sock_.them_c_str())) {
1873 spf_request_helo.set_ipv6_str(sock_.them_c_str());
1874 spf_request_mailfrom.set_ipv6_str(sock_.them_c_str());
1876 else {
1877 LOG(FATAL) << "bogus address " << sock_.them_address_literal() << ", "
1878 << sock_.them_c_str();
1881 auto const from{static_cast<std::string>(sender)};
1883 // Helo id
1884 spf_request_helo.set_helo_dom(client_identity_.ascii().c_str());
1886 auto const spf_res_helo{SPF::Response{spf_request_helo}};
1887 spf_result_helo_ = spf_res_helo.result();
1888 spf_received_helo_ = spf_res_helo.received_spf();
1889 spf_sender_domain_helo_ = spf_request_helo.get_sender_dom();
1891 if (spf_result_helo_ == SPF::Result::FAIL) {
1892 LOG(WARNING) << spf_res_helo.header_comment();
1894 else {
1895 LOG(INFO) << spf_res_helo.header_comment();
1898 // MailFrom
1899 spf_request_mailfrom.set_env_from(from.c_str());
1901 auto const spf_res_mailfrom{SPF::Response{spf_request_mailfrom}};
1902 spf_result_mailfrom_ = spf_res_mailfrom.result();
1903 spf_received_mailfrom_ = spf_res_mailfrom.received_spf();
1904 spf_sender_domain_mailfrom_ = spf_request_mailfrom.get_sender_dom();
1906 if (spf_result_mailfrom_ == SPF::Result::FAIL) {
1907 LOG(WARNING) << spf_res_mailfrom.header_comment();
1909 else {
1910 LOG(INFO) << spf_res_mailfrom.header_comment();
1913 if (spf_result_helo_ == SPF::Result::PASS) {
1914 if (lookup_domain(block_, spf_sender_domain_helo_)) {
1915 LOG(INFO) << "SPF sender domain (ehlo/helo " << spf_sender_domain_helo_
1916 << ") is blocked";
1917 return false;
1920 if (spf_result_mailfrom_ == SPF::Result::PASS) {
1921 if (lookup_domain(block_, spf_sender_domain_mailfrom_)) {
1922 LOG(INFO) << "SPF sender domain (MailFrom " << spf_sender_domain_mailfrom_
1923 << ") is blocked";
1924 return false;
1928 return true;
1931 bool Session::verify_from_params_(parameters_t const& parameters)
1933 // Take a look at the optional parameters:
1934 for (auto const& [name, value] : parameters) {
1935 if (iequal(name, "BODY")) {
1936 if (iequal(value, "8BITMIME")) {
1937 // everything is cool, this is our default...
1939 else if (iequal(value, "7BIT")) {
1940 // nothing to see here, move along...
1942 else if (iequal(value, "BINARYMIME")) {
1943 binarymime_ = true;
1945 else {
1946 LOG(WARNING) << "unrecognized BODY type \"" << value << "\" requested";
1949 else if (iequal(name, "SMTPUTF8")) {
1950 if (!value.empty()) {
1951 LOG(WARNING) << "SMTPUTF8 parameter has a value: " << value;
1953 smtputf8_ = true;
1956 // else if (iequal(name, "PRDR")) {
1957 // LOG(INFO) << "using PRDR";
1958 // prdr_ = true;
1959 // }
1961 else if (iequal(name, "SIZE")) {
1962 if (value.empty()) {
1963 LOG(WARNING) << "SIZE parameter has no value.";
1965 else {
1966 try {
1967 auto const sz = stoull(value);
1968 if (sz > max_msg_size()) {
1969 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1970 LOG(WARNING) << "SIZE parameter too large: " << sz;
1971 return false;
1974 catch (std::invalid_argument const& e) {
1975 LOG(WARNING) << "SIZE parameter has invalid value: " << value;
1977 catch (std::out_of_range const& e) {
1978 LOG(WARNING) << "SIZE parameter has out-of-range value: " << value;
1980 // I guess we just ignore bad size parameters.
1983 else if (iequal(name, "REQUIRETLS")) {
1984 if (!sock_.tls()) {
1985 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush;
1986 LOG(WARNING) << "REQUIRETLS needed";
1987 return false;
1990 else {
1991 LOG(WARNING) << "unrecognized 'MAIL FROM' parameter " << name << "="
1992 << value;
1996 return true;
1999 bool Session::verify_rcpt_params_(parameters_t const& parameters)
2001 // Take a look at the optional parameters:
2002 for (auto const& [name, value] : parameters) {
2003 if (iequal(name, "RRVS")) {
2004 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
2005 LOG(INFO) << name << "=" << value;
2007 else {
2008 LOG(WARNING) << "unrecognized 'RCPT TO' parameter " << name << "="
2009 << value;
2013 return true;
2016 // check recipient from RFC5321 RCPT TO:
2017 bool Session::verify_recipient_(Mailbox const& recipient)
2019 if ((recipient.local_part() == "Postmaster") && (recipient.domain() == "")) {
2020 LOG(INFO) << "magic Postmaster address";
2021 return true;
2024 auto const accepted_domain{[this, &recipient] {
2025 if (recipient.domain().is_address_literal()) {
2026 if (recipient.domain() != sock_.us_address_literal()) {
2027 LOG(WARNING) << "recipient.domain address " << recipient.domain()
2028 << " does not match ours " << sock_.us_address_literal();
2029 return false;
2031 return true;
2034 // Domains we accept mail for.
2035 if (accept_domains_.is_open()) {
2036 if (accept_domains_.contains(recipient.domain().ascii()) ||
2037 accept_domains_.contains(recipient.domain().utf8())) {
2038 return true;
2041 else {
2042 // If we have no list of domains to accept, at least take our own.
2043 if (recipient.domain() == server_id_()) {
2044 return true;
2048 return false;
2049 }()};
2051 if (!accepted_domain) {
2052 out_() << "554 5.7.1 relay access denied\r\n" << std::flush;
2053 LOG(WARNING) << "relay access denied for domain " << recipient.domain();
2054 return false;
2057 // Check for local addresses we reject.
2059 auto bad_recipients_db_name = config_path_ / "bad_recipients";
2060 CDB bad_recipients_db{bad_recipients_db_name};
2061 if (bad_recipients_db.contains(recipient.local_part())) {
2062 out_() << "550 5.1.1 bad recipient " << recipient << "\r\n" << std::flush;
2063 LOG(WARNING) << "bad recipient " << recipient;
2064 return false;
2069 auto temp_fail_db_name = config_path_ / "temp_fail";
2070 CDB temp_fail{temp_fail_db_name}; // Addresses we make wait...
2071 if (temp_fail.contains(recipient.local_part())) {
2072 out_() << "432 4.3.0 Recipient's incoming mail queue has been stopped\r\n"
2073 << std::flush;
2074 LOG(WARNING) << "temp fail for recipient " << recipient;
2075 return false;
2079 // Check for and act on magic "wait" address.
2081 using namespace boost::xpressive;
2083 mark_tag secs_(1);
2084 sregex const rex = icase("wait-rcpt-") >> (secs_ = +_d);
2085 smatch what;
2087 if (regex_match(recipient.local_part(), what, rex)) {
2088 auto const str = what[secs_].str();
2089 LOG(INFO) << "waiting at RCPT TO " << str << " seconds";
2090 long value = 0;
2091 std::from_chars(str.data(), str.data() + str.size(), value);
2092 sleep(value);
2093 LOG(INFO) << "done waiting";
2097 return true;