wait a little longer
[ghsmtp.git] / Session.cpp
blobb81eb529b73fc3bef04717b06b273bc4b9f2e799
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 "is_ascii.hpp"
18 #include "osutil.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>
28 #include <syslog.h>
30 DEFINE_string(selector, "ghsmtp", "DKIM selector");
32 using namespace std::string_literals;
34 namespace Config {
35 char const* wls[]{
36 "list.dnswl.org",
40 <https://www.dnswl.org/?page_id=15#query>
42 Return codes
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
59 12 – Healthcare
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
78 this situation.
82 char const* bls[]{
83 "b.barracudacentral.org",
84 "sbl.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 "dbl.spamhaus.org",
127 "multi.uribl.com",
130 constexpr auto greeting_wait = std::chrono::seconds{6};
131 constexpr int max_recipients_per_message = 100;
132 constexpr int max_unrecognized_cmds = 20;
134 // Read timeout value gleaned from RFC-1123 section 5.3.2 and RFC-5321
135 // section 4.5.3.2.7.
136 constexpr auto read_timeout = std::chrono::minutes{5};
137 constexpr auto write_timeout = std::chrono::seconds{30};
138 } // namespace Config
140 #include <gflags/gflags.h>
142 DEFINE_bool(test_mode, false, "ease up on some checks");
144 DEFINE_bool(immortal, false, "don't set process timout");
146 DEFINE_uint64(max_read, 0, "max data to read");
147 DEFINE_uint64(max_write, 0, "max data to write");
149 DEFINE_bool(rrvs, false, "support RRVS à la RFC 7293");
151 Session::Session(fs::path config_path,
152 std::function<void(void)> read_hook,
153 int fd_in,
154 int fd_out)
155 : config_path_(config_path)
156 , res_(config_path)
157 , sock_(fd_in, fd_out, read_hook, Config::read_timeout, Config::write_timeout)
158 //, send_(config_path, "smtp")
159 //, srs_(config_path)
161 auto accept_db_name = config_path_ / "accept_domains";
162 auto allow_db_name = config_path_ / "allow";
163 auto block_db_name = config_path_ / "block";
164 auto forward_db_name = config_path_ / "forward";
166 accept_domains_.open(accept_db_name);
167 allow_.open(allow_db_name);
168 block_.open(block_db_name);
169 forward_.open(forward_db_name);
171 if (sock_.has_peername() && !IP::is_private(sock_.us_c_str())) {
172 auto fcrdns = DNS::fcrdns(res_, sock_.us_c_str());
173 for (auto const& fcr : fcrdns) {
174 server_fcrdns_.emplace_back(fcr);
178 server_identity_ = [this] {
179 auto const id_from_env{getenv("GHSMTP_SERVER_ID")};
180 if (id_from_env)
181 return std::string{id_from_env};
183 auto const hostname{osutil::get_hostname()};
184 if (hostname.find('.') != std::string::npos)
185 return hostname;
187 if (!server_fcrdns_.empty()) {
188 // first result should be shortest
189 return server_fcrdns_.front().ascii();
192 auto const us_c_str = sock_.us_c_str();
193 if (us_c_str && !IP::is_private(us_c_str)) {
194 return IP::to_address_literal(us_c_str);
197 LOG(FATAL) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
198 return ""s;
199 }();
201 // send_.set_sender(server_identity_);
203 max_msg_size(Config::max_msg_size_initial);
206 void Session::max_msg_size(size_t max)
208 max_msg_size_ = max; // number to advertise via RFC 1870
210 if (FLAGS_max_read) {
211 sock_.set_max_read(FLAGS_max_read);
213 else {
214 auto const overhead = std::max(max / 10, size_t(2048));
215 sock_.set_max_read(max + overhead);
219 void Session::bad_host_(char const* msg) const
221 if (sock_.has_peername()) {
222 // On my systems, this pattern triggers a fail2ban rule that
223 // blocks connections from this IP address on port 25 for a few
224 // days. See <https://www.fail2ban.org/> for more info.
225 syslog(LOG_MAIL | LOG_WARNING, "bad host [%s] %s", sock_.them_c_str(), msg);
227 std::exit(EXIT_SUCCESS);
230 void Session::reset_()
232 // RSET does not force another EHLO/HELO, the one piece of per
233 // transaction data saved is client_identity_:
235 // client_identity_.clear(); <-- not cleared!
237 reverse_path_.clear();
238 forward_path_.clear();
239 spf_received_.clear();
240 // fwd_path_.clear();
241 // fwd_from_.clear();
242 // rep_info_.clear();
244 binarymime_ = false;
245 smtputf8_ = false;
246 // prdr_ = false;
248 if (msg_) {
249 msg_.reset();
252 max_msg_size(max_msg_size());
254 state_ = xact_step::mail;
255 // send_.rset();
258 // Return codes from connection establishment are 220 or 554, according
259 // to RFC 5321. That's it.
261 void Session::greeting()
263 CHECK(state_ == xact_step::helo);
265 if (sock_.has_peername()) {
266 close(2); // if we're a networked program, never send to stderr
268 std::string error_msg;
269 if (!verify_ip_address_(error_msg)) {
270 // no glog message at this point
271 bad_host_(error_msg.c_str());
274 /******************************************************************
275 <https://tools.ietf.org/html/rfc5321#section-4.3.1> says:
277 4.3. Sequencing of Commands and Replies
279 4.3.1. Sequencing Overview
281 The communication between the sender and receiver is an alternating
282 dialogue, controlled by the sender. As such, the sender issues a
283 command and the receiver responds with a reply. Unless other
284 arrangements are negotiated through service extensions, the sender
285 MUST wait for this response before sending further commands. One
286 important reply is the connection greeting. Normally, a receiver
287 will send a 220 "Service ready" reply when the connection is
288 completed. The sender SHOULD wait for this greeting message before
289 sending any commands.
291 So which is it?
293 “…the receiver responds with a reply.”
294 “…the sender MUST wait for this response…”
295 “One important reply is the connection greeting.”
296 “The sender SHOULD wait for this greeting…”
298 So is it MUST or SHOULD? I enforce MUST.
299 *******************************************************************/
301 // Wait a bit of time for pre-greeting traffic.
302 if (!(ip_allowed_ || fcrdns_allowed_)) {
303 if (sock_.input_ready(Config::greeting_wait)) {
304 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
305 // no glog message at this point
306 bad_host_("input before any greeting");
308 // Give a half greeting and wait again.
309 out_() << "220-" << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
310 if (sock_.input_ready(Config::greeting_wait)) {
311 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
312 // LOG(INFO) << "half greeting got " << client_;
313 bad_host_("input before full greeting");
316 LOG(INFO) << "connect from " << client_;
319 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
321 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr)) {
322 alarm(2 * 60); // initial alarm
326 void Session::flush() { out_() << std::flush; }
328 void Session::last_in_group_(std::string_view verb)
330 if (sock_.input_ready(std::chrono::seconds(0))) {
331 LOG(WARNING) << "pipelining error; input ready processing " << verb;
335 void Session::check_for_pipeline_error_(std::string_view verb)
337 if (!extensions_ && sock_.input_ready(std::chrono::seconds(0))) {
338 LOG(WARNING) << "pipelining error; input ready processing " << verb;
342 void Session::lo_(char const* verb, std::string_view client_identity)
344 last_in_group_(verb);
345 reset_();
347 if (client_identity_ != client_identity) {
348 client_identity_ = client_identity;
350 std::string error_msg;
351 if (!verify_client_(client_identity_, error_msg)) {
352 bad_host_(error_msg.c_str());
356 if (*verb == 'H') {
357 extensions_ = false;
358 out_() << "250 " << server_id_() << "\r\n";
361 if (*verb == 'E') {
362 extensions_ = true;
364 if (sock_.has_peername()) {
365 out_() << "250-" << server_id_() << " at your service, " << client_
366 << "\r\n";
368 else {
369 out_() << "250-" << server_id_() << "\r\n";
372 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
373 out_() << "250-8BITMIME\r\n"; // RFC 6152
375 if (FLAGS_rrvs) {
376 out_() << "250-RRVS\r\n"; // RFC 7293
379 // out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
381 if (sock_.tls()) {
382 // Check sasl sources for auth types.
383 // out_() << "250-AUTH PLAIN\r\n";
384 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
386 else {
387 // If we're not already TLS, offer TLS
388 out_() << "250-STARTTLS\r\n"; // RFC 3207
390 out_() << "250-ENHANCEDSTATUSCODES\r\n" // RFC 2034
391 "250-PIPELINING\r\n" // RFC 2920
392 "250-BINARYMIME\r\n" // RFC 3030
393 "250-CHUNKING\r\n" // RFC 3030
394 "250 SMTPUTF8\r\n"; // RFC 6531
397 out_() << std::flush;
399 if (sock_.has_peername()) {
400 if (std::find(begin(client_fcrdns_), end(client_fcrdns_),
401 client_identity_) != end(client_fcrdns_)) {
402 LOG(INFO) << verb << " " << client_identity << " from "
403 << sock_.them_address_literal();
405 else {
406 LOG(INFO) << verb << " " << client_identity << " from " << client_;
409 else {
410 LOG(INFO) << verb << " " << client_identity;
414 void Session::mail_from(Mailbox&& reverse_path, parameters_t const& parameters)
416 check_for_pipeline_error_("MAIL FROM");
418 switch (state_) {
419 case xact_step::helo:
420 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
421 LOG(WARNING) << "'MAIL FROM' before HELO/EHLO"
422 << (sock_.has_peername() ? " from " : "") << client_;
423 return;
424 case xact_step::mail: break;
425 case xact_step::rcpt:
426 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
427 LOG(WARNING) << "nested MAIL command"
428 << (sock_.has_peername() ? " from " : "") << client_;
429 return;
430 case xact_step::data:
431 case xact_step::bdat:
432 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush;
433 LOG(WARNING) << "nested MAIL command"
434 << (sock_.has_peername() ? " from " : "") << client_;
435 return;
436 case xact_step::rset:
437 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
438 LOG(WARNING) << "error state must be cleared with a RSET"
439 << (sock_.has_peername() ? " from " : "") << client_;
440 return;
443 if (!verify_from_params_(parameters)) {
444 return;
447 if (!smtputf8_ && !is_ascii(reverse_path.local_part())) {
448 LOG(WARNING) << "non ascii reverse_path \"" << reverse_path
449 << "\" without SMTPUTF8 paramater";
452 std::string error_msg;
453 if (!verify_sender_(reverse_path, error_msg)) {
454 LOG(WARNING) << "verify sender failed: " << error_msg;
455 bad_host_(error_msg.c_str());
458 reverse_path_ = std::move(reverse_path);
459 // fwd_path_.clear();
460 // fwd_from_.clear();
461 forward_path_.clear();
462 out_() << "250 2.1.0 MAIL FROM OK\r\n";
463 // No flush RFC-2920 section 3.1, this could be part of a command group.
465 fmt::memory_buffer params;
466 for (auto const& [name, value] : parameters) {
467 fmt::format_to(params, " {}", name);
468 if (!value.empty()) {
469 fmt::format_to(params, "={}", value);
472 LOG(INFO) << "MAIL FROM:<" << reverse_path_ << ">" << fmt::to_string(params);
474 state_ = xact_step::rcpt;
477 // bool Session::forward_to_(std::string const& forward, Mailbox const& rcpt_to)
478 // {
479 // // If we're already forwarding or replying, reject
480 // if (!fwd_path_.empty() || !rep_info_.empty()) {
481 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
482 // stopped\r\n"
483 // << std::flush;
484 // LOG(WARNING) << "failed to forward to <" << forward
485 // << "> already forwarding or replying for: " << rcpt_to;
486 // return false;
487 // }
489 // fwd_path_ = Mailbox(forward);
490 // fwd_from_ = rcpt_to;
492 // // New bounce address
493 // Reply::from_to bounce;
494 // bounce.mail_from = reverse_path_.as_string();
496 // auto const new_bounce = srs_.enc_bounce(bounce, server_id_().c_str());
498 // auto const mail_from = Mailbox(new_bounce);
500 // std::string error_msg;
501 // if (!send_.mail_from_rcpt_to(res_, mail_from, fwd_path_, error_msg)) {
502 // out_() << error_msg << std::flush;
503 // LOG(WARNING) << "failed to forward <" << fwd_path_ << "> " << error_msg;
504 // return false;
505 // }
507 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> forwarding to == <" << fwd_path_
508 // << ">";
509 // return true;
510 // }
512 // bool Session::reply_to_(Reply::from_to const& reply_info, Mailbox const&
513 // rcpt_to)
514 // {
515 // // If we're already forwarding or replying, reject
516 // if (!fwd_path_.empty() || !rep_info_.empty()) {
517 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
518 // stopped\r\n"
519 // << std::flush;
520 // LOG(WARNING) << "failed to reply to <" << reply_info.mail_from
521 // << "> already forwarding or replying for: " << rcpt_to;
522 // return false;
523 // }
525 // rep_info_ = reply_info;
527 // Mailbox const from(rep_info_.rcpt_to_local_part, server_identity_);
528 // Mailbox const to(rep_info_.mail_from);
530 // std::string error_msg;
531 // if (!send_.mail_from_rcpt_to(res_, from, to, error_msg)) {
532 // out_() << error_msg << std::flush;
533 // LOG(WARNING) << "failed to reply from <" << from << "> to <" << to << ">
534 // "
535 // << error_msg;
536 // return false;
537 // }
539 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> is a reply to "
540 // << rep_info_.mail_from << " from " <<
541 // rep_info_.rcpt_to_local_part;
542 // return true;
543 // }
545 void Session::rcpt_to(Mailbox&& forward_path, parameters_t const& parameters)
547 check_for_pipeline_error_("RCPT TO");
549 switch (state_) {
550 case xact_step::helo:
551 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
552 LOG(WARNING) << "'RCPT TO' before HELO/EHLO"
553 << (sock_.has_peername() ? " from " : "") << client_;
554 return;
555 case xact_step::mail:
556 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
557 LOG(WARNING) << "'RCPT TO' before 'MAIL FROM'"
558 << (sock_.has_peername() ? " from " : "") << client_;
559 return;
560 case xact_step::rcpt:
561 case xact_step::data: break;
562 case xact_step::bdat:
563 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
564 LOG(WARNING) << "'RCPT TO' during BDAT transfer"
565 << (sock_.has_peername() ? " from " : "") << client_;
566 return;
567 case xact_step::rset:
568 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
569 LOG(WARNING) << "error state must be cleared with a RSET"
570 << (sock_.has_peername() ? " from " : "") << client_;
571 return;
574 if (!verify_rcpt_params_(parameters))
575 return;
577 if (!verify_recipient_(forward_path))
578 return;
580 if (!smtputf8_ && !is_ascii(forward_path.local_part())) {
581 LOG(WARNING) << "non ascii forward_path \"" << forward_path
582 << "\" without SMTPUTF8 paramater";
585 if (forward_path_.size() >= Config::max_recipients_per_message) {
586 out_() << "452 4.5.3 too many recipients\r\n" << std::flush;
587 LOG(WARNING) << "too many recipients <" << forward_path << ">";
588 return;
590 // no check for dups, postfix doesn't
591 forward_path_.emplace_back(std::move(forward_path));
593 Mailbox const& rcpt_to_mbx = forward_path_.back();
595 LOG(INFO) << "RCPT TO:<" << rcpt_to_mbx << ">";
597 // auto const rcpt_to_str = rcpt_to_mbx.as_string();
599 // if (auto reply = srs_.dec_reply(rcpt_to_mbx.local_part()); reply) {
600 // if (!reply_to_(*reply, rcpt_to_mbx))
601 // return;
602 // }
603 // else if (auto const forward = forward_.find(rcpt_to_str.c_str()); forward)
604 // {
605 // if (!forward_to_(*forward, rcpt_to_mbx))
606 // return;
607 // }
608 // else {
609 // LOG(INFO) << "RCPT TO:<" << rcpt_to_str << ">";
610 // }
612 // No flush RFC-2920 section 3.1, this could be part of a command group.
613 out_() << "250 2.1.5 RCPT TO OK\r\n";
615 state_ = xact_step::data;
618 // The headers Received and Received-SPF are returned as a string.
620 std::string Session::added_headers_(MessageStore const& msg)
622 auto const protocol{[this]() {
623 if (sock_.tls() && !extensions_) {
624 LOG(WARNING) << "TLS active without extensions";
626 // <https://www.iana.org/assignments/mail-parameters/mail-parameters.xhtml#mail-parameters-5>
627 if (smtputf8_)
628 return sock_.tls() ? "UTF8SMTPS" : "UTF8SMTP";
629 else if (sock_.tls())
630 return "ESMTPS";
631 else if (extensions_)
632 return "ESMTP";
633 else
634 return "SMTP";
635 }()};
637 fmt::memory_buffer headers;
639 // Return-Path:
640 fmt::format_to(headers, "Return-Path: <{}>\r\n", reverse_path_);
642 // Received-SPF:
643 if (!spf_received_.empty()) {
644 fmt::format_to(headers, "{}\r\n", spf_received_);
647 // Received:
648 // <https://tools.ietf.org/html/rfc5321#section-4.4>
649 fmt::format_to(headers, "Received: from {}", client_identity_.utf8());
650 if (sock_.has_peername()) {
651 fmt::format_to(headers, " ({})", client_);
653 fmt::format_to(headers, "\r\n\tby {} with {} id {}", server_identity_.utf8(),
654 protocol, msg.id());
655 if (forward_path_.size()) {
656 fmt::format_to(headers, "\r\n\tfor <{}>", forward_path_[0]);
657 for (auto i = 1u; i < forward_path_.size(); ++i)
658 fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
660 std::string const tls_info{sock_.tls_info()};
661 if (tls_info.length()) {
662 fmt::format_to(headers, "\r\n\t({})", tls_info);
664 fmt::format_to(headers, ";\r\n\t{}\r\n", msg.when());
666 return fmt::to_string(headers);
669 namespace {
670 bool lookup_domain(CDB& cdb, Domain const& domain)
672 if (!domain.empty()) {
673 if (cdb.contains(domain.ascii())) {
674 return true;
676 if (domain.is_unicode() && cdb.contains(domain.utf8())) {
677 return true;
680 return false;
682 } // namespace
684 std::tuple<Session::SpamStatus, std::string> Session::spam_status_()
686 if (spf_result_ == SPF::Result::FAIL && !ip_allowed_)
687 return {SpamStatus::spam, "SPF failed"};
689 // These should have already been rejected by verify_client_().
690 if ((reverse_path_.domain() == "localhost.local") ||
691 (reverse_path_.domain() == "localhost"))
692 return {SpamStatus::spam, "bogus reverse_path"};
694 std::vector<std::string> why_ham;
696 // Anything enciphered tastes a lot like ham.
697 if (sock_.tls())
698 why_ham.emplace_back("they used TLS");
700 if (spf_result_ == SPF::Result::PASS) {
701 if (lookup_domain(allow_, spf_sender_domain_)) {
702 why_ham.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
703 spf_sender_domain_.utf8()));
705 else {
706 auto tld_dom{tld_db_.get_registered_domain(spf_sender_domain_.ascii())};
707 if (tld_dom && allow_.contains(tld_dom)) {
708 why_ham.emplace_back(fmt::format(
709 "SPF sender registered domain ({}) is allowed", tld_dom));
714 if (fcrdns_allowed_)
715 why_ham.emplace_back(
716 fmt::format("FCrDNS (or it's registered domain) is allowed"));
718 if (!why_ham.empty())
719 return {SpamStatus::ham,
720 fmt::format("{}", fmt::join(std::begin(why_ham), std::end(why_ham),
721 ", and "))};
723 return {SpamStatus::spam, "it's not ham"};
726 static std::string folder(Session::SpamStatus status,
727 std::vector<Mailbox> const& forward_path,
728 Mailbox const& reverse_path)
730 if (status == Session::SpamStatus::spam)
731 return ".Junk";
733 return "";
736 bool Session::msg_new()
738 CHECK((state_ == xact_step::data) || (state_ == xact_step::bdat));
740 auto const& [status, reason]{spam_status_()};
742 LOG(INFO) << ((status == SpamStatus::ham) ? "ham since " : "spam since ")
743 << reason;
745 // All sources of ham get a fresh 5 minute timeout per message.
746 if (status == SpamStatus::ham) {
747 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr))
748 alarm(5 * 60);
751 msg_ = std::make_unique<MessageStore>();
753 if (!FLAGS_max_write)
754 FLAGS_max_write = max_msg_size();
756 try {
757 msg_->open(server_id_(), FLAGS_max_write,
758 folder(status, forward_path_, reverse_path_));
759 auto const hdrs{added_headers_(*(msg_.get()))};
760 msg_->write(hdrs);
762 // fmt::memory_buffer spam_status;
763 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
764 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
765 // msg_->write(spam_status.data(), spam_status.size());
767 LOG(INFO) << "Spam-Status: "
768 << ((status == SpamStatus::spam) ? "Yes" : "No") << ", "
769 << reason;
771 return true;
773 catch (std::system_error const& e) {
774 switch (errno) {
775 case ENOSPC:
776 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
777 LOG(ERROR) << "no space";
778 msg_->trash();
779 msg_.reset();
780 return false;
782 default:
783 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
784 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
785 LOG(ERROR) << e.what();
786 msg_->trash();
787 msg_.reset();
788 return false;
791 catch (std::exception const& e) {
792 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
793 LOG(ERROR) << e.what();
794 msg_->trash();
795 msg_.reset();
796 return false;
799 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
800 LOG(ERROR) << "msg_new failed with no exception caught";
801 msg_->trash();
802 msg_.reset();
803 return false;
806 bool Session::msg_write(char const* s, std::streamsize count)
808 if ((state_ != xact_step::data) && (state_ != xact_step::bdat))
809 return false;
811 if (!msg_)
812 return false;
814 try {
815 if (msg_->write(s, count))
816 return true;
818 catch (std::system_error const& e) {
819 switch (errno) {
820 case ENOSPC:
821 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
822 LOG(ERROR) << "no space";
823 msg_->trash();
824 msg_.reset();
825 return false;
827 default:
828 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
829 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
830 LOG(ERROR) << e.what();
831 msg_->trash();
832 msg_.reset();
833 return false;
836 catch (std::exception const& e) {
837 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
838 LOG(ERROR) << e.what();
839 msg_->trash();
840 msg_.reset();
841 return false;
844 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
845 LOG(ERROR) << "msg_write failed with no exception caught";
846 msg_->trash();
847 msg_.reset();
848 return false;
851 bool Session::data_start()
853 last_in_group_("DATA");
855 switch (state_) {
856 case xact_step::helo:
857 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
858 LOG(WARNING) << "'DATA' before HELO/EHLO"
859 << (sock_.has_peername() ? " from " : "") << client_;
860 return false;
861 case xact_step::mail:
862 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
863 LOG(WARNING) << "'DATA' before 'MAIL FROM'"
864 << (sock_.has_peername() ? " from " : "") << client_;
865 return false;
866 case xact_step::rcpt:
868 /******************************************************************
869 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
871 The DATA command can fail at only two points in the protocol exchange:
873 If there was no MAIL, or no RCPT, command, or all such commands were
874 rejected, the server MAY return a "command out of sequence" (503) or
875 "no valid recipients" (554) reply in response to the DATA command.
877 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
879 The additional restriction is that when there have been no successful
880 RCPT commands in the mail transaction, the DATA command MUST fail
881 with a 503 reply code.
883 Therefore I will send the reply code that is valid for both, and
884 do the same for the BDAT case.
885 *******************************************************************/
887 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
888 LOG(WARNING) << "no valid recipients"
889 << (sock_.has_peername() ? " from " : "") << client_;
890 return false;
891 case xact_step::data: break;
892 case xact_step::bdat:
893 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
894 LOG(WARNING) << "'DATA' during BDAT transfer"
895 << (sock_.has_peername() ? " from " : "") << client_;
896 return false;
897 case xact_step::rset:
898 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
899 LOG(WARNING) << "error state must be cleared with a RSET"
900 << (sock_.has_peername() ? " from " : "") << client_;
901 return false;
904 if (binarymime_) {
905 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
906 << std::flush;
907 LOG(WARNING) << "DATA does not support BINARYMIME";
908 state_ = xact_step::rset; // RFC 3030 section 3 page 5
909 return false;
912 if (!msg_new()) {
913 LOG(ERROR) << "msg_new() failed";
914 return false;
917 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush;
918 LOG(INFO) << "DATA";
919 return true;
922 // bool Session::do_forward_(message::parsed& msg)
923 // {
924 // auto msg_fwd = msg;
926 // // Generate a reply address
927 // Reply::from_to reply;
928 // reply.mail_from = msg_fwd.dmarc_from;
929 // reply.rcpt_to_local_part = fwd_from_.local_part();
931 // auto const reply_addr =
932 // fmt::format("{}@{}", srs_.enc_reply(reply), server_id_());
934 // auto const munging = false;
936 // auto const sender = server_identity_.ascii().c_str();
937 // auto const selector = FLAGS_selector.c_str();
938 // auto const key_file =
939 // (config_path_ / FLAGS_selector).replace_extension("private");
940 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
942 // if (munging) {
943 // auto const from_hdr =
944 // fmt::format("From: \"{} via\" <@>", msg_fwd.dmarc_from, reply_addr);
945 // message::rewrite_from_to(msg_fwd, from_hdr, "", sender, selector,
946 // key_file);
947 // }
948 // else {
949 // auto const reply_to_hdr = fmt::format("Reply-To: {}", reply_addr);
950 // message::rewrite_from_to(msg_fwd, "", reply_to_hdr, sender, selector,
951 // key_file);
952 // }
954 // // Forward it on
955 // if (!send_.send(msg_fwd.as_string())) {
956 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
957 // "stopped\r\n"
958 // << std::flush;
960 // LOG(ERROR) << "failed to send for " << fwd_path_;
961 // return false;
962 // }
964 // LOG(INFO) << "successfully sent for " << fwd_path_;
965 // return true;
966 // }
968 // bool Session::do_reply_(message::parsed& msg)
969 // {
970 // Mailbox to_mbx(rep_info_.mail_from);
971 // Mailbox from_mbx(rep_info_.rcpt_to_local_part, server_identity_);
973 // auto reply = std::make_unique<MessageStore>();
974 // reply->open(server_id_(), FLAGS_max_write, ".Drafts");
976 // auto const date{Now{}};
977 // auto const pill{Pill{}};
978 // auto const mid_str =
979 // fmt::format("<{}.{}@{}>", date.sec(), pill, server_identity_);
981 // fmt::memory_buffer bfr;
983 // fmt::format_to(bfr, "From: <{}>\r\n", from_mbx);
984 // fmt::format_to(bfr, "To: <{}>\r\n", to_mbx);
986 // fmt::format_to(bfr, "Date: {}\r\n", date.c_str());
988 // fmt::format_to(bfr, "Message-ID: {}\r\n", mid_str.c_str());
990 // if (!msg.get_header(message::Subject).empty()) {
991 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
992 // msg.get_header(message::Subject));
993 // }
994 // else {
995 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
996 // "Reply to your message");
997 // }
999 // if (!msg.get_header(message::In_Reply_To).empty()) {
1000 // fmt::format_to(bfr, "{}: {}\r\n", message::In_Reply_To,
1001 // msg.get_header(message::In_Reply_To));
1002 // }
1004 // if (!msg.get_header(message::MIME_Version).empty() &&
1005 // msg.get_header(message::Content_Type).empty()) {
1006 // fmt::format_to(bfr, "{}: {}\r\n", message::MIME_Version,
1007 // msg.get_header(message::MIME_Version));
1008 // fmt::format_to(bfr, "{}: {}\r\n", message::Content_Type,
1009 // msg.get_header(message::Content_Type));
1010 // }
1012 // reply->write(fmt::to_string(bfr));
1014 // if (!msg.body.empty()) {
1015 // reply->write("\r\n");
1016 // reply->write(msg.body);
1017 // }
1019 // auto const msg_data = reply->freeze();
1020 // message::parsed msg_reply;
1021 // CHECK(msg_reply.parse(msg_data));
1023 // auto const sender = server_identity_.ascii().c_str();
1024 // auto const selector = FLAGS_selector.c_str();
1025 // auto const key_file =
1026 // (config_path_ / FLAGS_selector).replace_extension("private");
1027 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1029 // message::dkim_sign(msg_reply, sender, selector, key_file);
1031 // if (!send_.send(msg_reply.as_string())) {
1032 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1033 // "stopped\r\n"
1034 // << std::flush;
1036 // LOG(ERROR) << "send failed for reply to " << to_mbx << " from " <<
1037 // from_mbx; return false;
1038 // }
1040 // LOG(INFO) << "successful reply to " << to_mbx << " from " << from_mbx;
1041 // return true;
1042 // }
1044 bool Session::do_deliver_()
1046 CHECK(msg_);
1048 // auto const sender = server_identity_.ascii().c_str();
1049 // auto const selector = FLAGS_selector.c_str();
1050 // auto const key_file =
1051 // (config_path_ / FLAGS_selector).replace_extension("private");
1052 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1054 try {
1055 // auto const msg_data = msg_->freeze();
1057 // message::parsed msg;
1059 // // Only deal in RFC-5322 Mail Objects.
1060 // bool const message_parsed = msg.parse(msg_data);
1061 // if (message_parsed) {
1063 // // remove any Return-Path
1064 // message::remove_delivery_headers(msg);
1066 // auto const authentic =
1067 // message_parsed &&
1068 // message::authentication(msg, sender, selector, key_file);
1070 // // write a new Return-Path
1071 // msg_->write(fmt::format("Return-Path: <{}>\r\n", reverse_path_));
1073 // for (auto const h : msg.headers) {
1074 // msg_->write(h.as_string());
1075 // msg_->write("\r\n");
1076 // }
1077 // if (!msg.body.empty()) {
1078 // msg_->write("\r\n");
1079 // msg_->write(msg.body);
1080 // }
1082 msg_->deliver();
1084 // if (authentic && !fwd_path_.empty()) {
1085 // if (!do_forward_(msg))
1086 // return false;
1087 // }
1088 // if (authentic && !rep_info_.empty()) {
1089 // if (!do_reply_(msg))
1090 // return false;
1091 // }
1092 // }
1094 msg_->close();
1096 catch (std::system_error const& e) {
1097 switch (errno) {
1098 case ENOSPC:
1099 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
1100 LOG(ERROR) << "no space";
1101 msg_->trash();
1102 reset_();
1103 return false;
1105 default:
1106 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
1107 if (errno)
1108 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
1109 LOG(ERROR) << e.what();
1110 msg_->trash();
1111 reset_();
1112 return false;
1116 return true;
1119 void Session::data_done()
1121 CHECK((state_ == xact_step::data));
1123 if (msg_ && msg_->size_error()) {
1124 data_size_error();
1125 return;
1128 do_deliver_();
1130 // if (prdr_) {
1131 // out_() << "353\r\n";
1132 // for (auto fp : forward_path_) {
1133 // out_() << "250 2.1.5 RCPT TO OK\r\n";
1134 // }
1135 // }
1137 // Check for and act on magic "wait" address.
1139 using namespace boost::xpressive;
1141 mark_tag secs_(1);
1142 sregex const rex = icase("wait-data-") >> (secs_ = +_d);
1143 smatch what;
1145 for (auto fp : forward_path_) {
1146 if (regex_match(fp.local_part(), what, rex)) {
1147 auto const str = what[secs_].str();
1148 LOG(INFO) << "waiting at DATA " << str << " seconds";
1149 long value = 0;
1150 std::from_chars(str.data(), str.data() + str.size(), value);
1151 sleep(value);
1152 LOG(INFO) << "done waiting";
1157 out_() << "250 2.0.0 DATA OK\r\n" << std::flush;
1158 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1159 << msg_->id();
1161 reset_();
1164 void Session::data_size_error()
1166 out_().clear(); // clear possible eof from input side
1167 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1168 if (msg_) {
1169 msg_->trash();
1171 LOG(WARNING) << "DATA size error";
1172 reset_();
1175 void Session::data_error()
1177 out_().clear(); // clear possible eof from input side
1178 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush;
1179 if (msg_) {
1180 msg_->trash();
1182 LOG(WARNING) << "DATA error";
1183 reset_();
1186 bool Session::bdat_start(size_t n)
1188 // In practice, this one gets pipelined.
1189 // last_in_group_("BDAT");
1191 switch (state_) {
1192 case xact_step::helo:
1193 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
1194 LOG(WARNING) << "'BDAT' before HELO/EHLO"
1195 << (sock_.has_peername() ? " from " : "") << client_;
1196 return false;
1197 case xact_step::mail:
1198 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
1199 LOG(WARNING) << "'BDAT' before 'MAIL FROM'"
1200 << (sock_.has_peername() ? " from " : "") << client_;
1201 return false;
1202 case xact_step::rcpt:
1203 // See comment in data_start()
1204 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
1205 LOG(WARNING) << "no valid recipients"
1206 << (sock_.has_peername() ? " from " : "") << client_;
1207 return false;
1208 case xact_step::data: // first bdat
1209 break;
1210 case xact_step::bdat: return true;
1211 case xact_step::rset:
1212 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
1213 LOG(WARNING) << "error state must be cleared with a RSET"
1214 << (sock_.has_peername() ? " from " : "") << client_;
1215 return false;
1218 state_ = xact_step::bdat;
1220 return msg_new();
1223 void Session::bdat_done(size_t n, bool last)
1225 if (state_ != xact_step::bdat) {
1226 bdat_seq_error();
1227 return;
1230 if (!msg_) {
1231 return;
1234 if (msg_->size_error()) {
1235 bdat_size_error();
1236 return;
1239 if (!last) {
1240 out_() << "250 2.0.0 BDAT " << n << " OK\r\n" << std::flush;
1241 LOG(INFO) << "BDAT " << n;
1242 return;
1245 do_deliver_();
1247 out_() << "250 2.0.0 BDAT " << n << " LAST OK\r\n" << std::flush;
1249 LOG(INFO) << "BDAT " << n << " LAST";
1250 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1251 << msg_->id();
1252 reset_();
1255 void Session::bdat_size_error()
1257 out_().clear(); // clear possible eof from input side
1258 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1259 if (msg_) {
1260 msg_->trash();
1262 LOG(WARNING) << "BDAT size error";
1263 reset_();
1266 void Session::bdat_seq_error()
1268 out_().clear(); // clear possible eof from input side
1269 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush;
1270 if (msg_) {
1271 msg_->trash();
1273 LOG(WARNING) << "BDAT sequence error";
1274 reset_();
1277 void Session::bdat_io_error()
1279 out_().clear(); // clear possible eof from input side
1280 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush;
1281 if (msg_) {
1282 msg_->trash();
1284 LOG(WARNING) << "BDAT I/O error";
1285 reset_();
1288 void Session::rset()
1290 out_() << "250 2.1.5 RSET OK\r\n";
1291 // No flush RFC-2920 section 3.1, this could be part of a command group.
1292 LOG(INFO) << "RSET";
1293 reset_();
1296 void Session::noop(std::string_view str)
1298 last_in_group_("NOOP");
1299 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush;
1300 LOG(INFO) << "NOOP" << (str.length() ? " " : "") << str;
1303 void Session::vrfy(std::string_view str)
1305 last_in_group_("VRFY");
1306 out_() << "252 2.1.5 try it\r\n" << std::flush;
1307 LOG(INFO) << "VRFY" << (str.length() ? " " : "") << str;
1310 void Session::help(std::string_view str)
1312 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n" << std::flush;
1313 LOG(INFO) << "HELP" << (str.length() ? " " : "") << str;
1316 void Session::quit()
1318 // send_.quit();
1319 // last_in_group_("QUIT");
1320 out_() << "221 2.0.0 closing connection\r\n" << std::flush;
1321 LOG(INFO) << "QUIT";
1322 exit_();
1325 void Session::auth()
1327 out_() << "454 4.7.0 authentication failure\r\n" << std::flush;
1328 LOG(INFO) << "AUTH";
1329 bad_host_("auth");
1332 void Session::error(std::string_view log_msg)
1334 out_() << "421 4.3.5 system error: " << log_msg << "\r\n" << std::flush;
1335 LOG(WARNING) << log_msg;
1338 void Session::cmd_unrecognized(std::string_view cmd)
1340 auto const escaped{esc(cmd)};
1341 LOG(WARNING) << "command unrecognized: \"" << escaped << "\"";
1343 if (++n_unrecognized_cmds_ >= Config::max_unrecognized_cmds) {
1344 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1345 << "\" exceeds limit\r\n"
1346 << std::flush;
1347 LOG(WARNING) << n_unrecognized_cmds_
1348 << " unrecognized commands is too many";
1349 exit_();
1352 out_() << "500 5.5.1 command unrecognized: \"" << escaped << "\"\r\n"
1353 << std::flush;
1356 void Session::bare_lf()
1358 // Error code used by Office 365.
1359 out_() << "554 5.6.11 bare LF\r\n" << std::flush;
1360 LOG(WARNING) << "bare LF";
1361 exit_();
1364 void Session::max_out()
1366 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1367 LOG(WARNING) << "message size maxed out";
1368 exit_();
1371 void Session::time_out()
1373 out_() << "421 4.4.2 time-out\r\n" << std::flush;
1374 LOG(WARNING) << "time-out" << (sock_.has_peername() ? " from " : "")
1375 << client_;
1376 exit_();
1379 void Session::starttls()
1381 last_in_group_("STARTTLS");
1382 if (sock_.tls()) {
1383 out_() << "554 5.5.1 TLS already active\r\n" << std::flush;
1384 LOG(WARNING) << "STARTTLS issued with TLS already active";
1386 else if (!extensions_) {
1387 out_() << "554 5.5.1 TLS not avaliable without using EHLO\r\n"
1388 << std::flush;
1389 LOG(WARNING) << "STARTTLS issued without using EHLO";
1391 else {
1392 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush;
1393 if (sock_.starttls_server(config_path_)) {
1394 reset_();
1395 max_msg_size(Config::max_msg_size_bro);
1396 LOG(INFO) << "STARTTLS " << sock_.tls_info();
1398 else {
1399 LOG(INFO) << "failed STARTTLS";
1404 void Session::exit_()
1406 // sock_.log_totals();
1408 timespec time_used{};
1409 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time_used);
1411 LOG(INFO) << "CPU time " << time_used.tv_sec << "." << std::setw(9)
1412 << std::setfill('0') << time_used.tv_nsec << " seconds";
1414 std::exit(EXIT_SUCCESS);
1417 namespace {
1418 bool ip4_allowed(char const* addr)
1420 struct nw {
1421 char const* net;
1422 char const* mask;
1423 char const* comment;
1426 // clang-format off
1428 // 255 0b11111111 8
1429 // 254 0b11111110 7
1430 // 252 0b11111100 6
1431 // 248 0b11111000 5
1432 // 240 0b11110000 4
1433 // 224 0b11100000 3
1434 // 192 0b11000000 2
1435 // 128 0b10000000 1
1437 nw const networks[]{
1438 // the one very special case
1439 {"108.83.36.112", "255.255.255.248", "108.83.36.112/29"},
1441 // accept from major providers:
1442 {"3.0.0.0", "255.0.0.0", "3.0.0.0/9 and 3.128.0.0/9 Amazon"},
1443 {"5.45.198.0", "255.255.254.0", "5.45.198.0/23 YANDEX-5-45-198"},
1444 {"12.153.224.0", "255.255.255.0", "12.153.224.0/24 E-TRADE10-224"},
1445 {"13.108.0.0", "255.252.0.0", "13.108.0.0/14 SALESF-3 NET-13-108-0-0-1"},
1446 {"17.0.0.0", "255.0.0.0", "17.0.0.0/8 APPLE-WWNET"},
1447 {"20.67.221.0", "255.255.255.0", "20.64.0.0/10 NET-20-33-0-0-1 duck.com"},
1448 {"20.67.222.0", "255.255.255.0", "20.64.0.0/10 NET-20-33-0-0-1 duck.com"},
1449 {"20.67.223.0", "255.255.255.0", "20.64.0.0/10 NET-20-33-0-0-1 duck.com"},
1450 {"40.74.0.0", "255.254.0.0", "40.74.0.0/15 MSFT NET-40-74-0-0-1"},
1451 {"40.76.0.0", "255.252.0.0", "40.76.0.0/14 MSFT NET-40-74-0-0-1"},
1452 {"40.80.0.0", "255.240.0.0", "40.80.0.0/12 MSFT NET-40-74-0-0-1"},
1453 {"40.92.0.0", "255.252.0.0", "40.96.0.0/14 MSFT NET-40-74-0-0-1"},
1454 {"40.96.0.0", "255.240.0.0", "40.96.0.0/12 MSFT NET-40-74-0-0-1"},
1455 {"40.112.0.0", "255.248.0.0", "40.112.0.0/13 MSFT NET-40-74-0-0-1"},
1456 {"40.120.0.0", "255.252.0.0", "40.120.0.0/14 MSFT NET-40-74-0-0-1"},
1457 {"40.124.0.0", "255.255.0.0", "40.124.0.0/16 MSFT NET-40-74-0-0-1"},
1458 {"40.125.0.0", "255.255.128.0", "40.125.0.0/17 MSFT NET-40-74-0-0-1"},
1459 {"56.0.0.0", "255.0.0.0", "56.0.0.0/8 USPS1"},
1460 {"65.52.0.0", "255.252.0.0", "65.52.0.0/14 MICROSOFT-1BLK"},
1461 {"65.196.177.0", "255.255.255.0", "UU-65-196-177 E*TRADE FINANCIAL CORPORATION (C05251409)"},
1462 {"66.163.160.0", "255.255.224.0", "66.163.160.0/19 A-YAHOO-US2"},
1463 {"66.211.176.0", "255.255.240.0", "66.211.176.0/20 EBAY-2"},
1464 {"66.211.172.0", "255.255.252.0", "66.211.172.0/22 EBAY-2"},
1465 {"66.220.144.0", "255.255.240.0", "66.220.144.0/20 TFBNET3"},
1466 {"68.232.192.0", "255.255.240.0", "68.232.192.0/20 EXACT-IP-NET-2"},
1467 {"69.171.224.0", "255.255.224.0", "69.171.224.0/19 TFBNET3"},
1468 {"70.47.67.0", "255.255.255.0", "70.47.67.0/24 NET-462F4300-24"},
1469 {"74.6.0.0", "255.255.0.0", "INKTOMI-BLK-6 Oath"},
1470 {"74.125.0.0", "255.255.0.0", "74.125.0.0/16 GOOGLE"},
1471 {"75.101.100.43", "255.255.255.255", "new.toad.com"},
1472 {"76.178.68.57", "255.255.255.255", "cpe-76-178-68-57.natsow.res.rr.com"},
1473 {"98.136.0.0", "255.252.0.0", "98.136.0.0/14 A-YAHOO-US9"},
1474 {"104.40.0.0", "255.248.0.0", "104.40.0.0/13 MSFT"},
1475 {"108.174.0.0", "255.255.240.0", "108.174.0.0/20 LINKEDIN"},
1476 {"159.45.0.0", "255.255.0.0", "159.45.0.0/16 AGE-COM"},
1477 {"159.53.0.0", "255.255.0.0", "159.53.0.0/16 JMC"},
1478 {"159.135.224.0", "255.255.240.0", "159.135.224.0/20 MNO87-159-135-224-0-0"},
1479 {"162.247.72.0", "255.255.252.0", "162.247.72.0/22 CALYX-INSTITUTE-V4-1"},
1480 {"165.107.0.0", "255.255.0.0", "NET-LDC-CA-GOV"},
1481 {"192.30.252.0", "255.255.252.0", "192.30.252.0/22 GITHUB-NET4-1"},
1482 {"192.175.128.0", "255.255.128.0", "192.175.128.0/17 NETBLK-VANGUARD"},
1483 {"198.2.128.0", "255.255.192.0", "198.2.128.0/18 RSG-DELIVERY"},
1484 {"198.252.206.0", "255.255.255.0", "198.252.206.0/24 SE-NET01"},
1485 {"199.122.120.0", "255.255.248.0", "199.122.120.0/21 EXACT-IP-NET-3"},
1486 {"204.13.164.0", "255.255.255.0", "204.13.164.0/24 RISEUP-NETWORKS-SWIFT-BLOCK2"},
1487 {"204.29.186.0", "255.255.254.0", "204.29.186.0/23 ATDN-NSCAPE"},
1488 {"205.139.104.0", "255.255.252.0", "205.139.104.0/22 SAVV-S259964-8"},
1489 {"205.201.128.0", "255.255.240.0", "205.201.128.0/20 RSG-DELIVERY"},
1490 {"208.118.235.0", "255.255.255.0", "208.118.235.0/24 TWDX-208-118-235-0-1"},
1491 {"208.192.0.0", "255.192.0.0", "208.192.0.0/10 UUNET1996B"},
1492 {"209.51.188.0", "255.255.255.0", "I:NET-209.51.188.0/24 FSF"},
1493 {"209.85.128.0", "255.255.128.0", "209.85.128.0/17 GOOGLE"},
1494 {"209.132.176.0", "255.255.240.0", "209.132.176.0/20 RED-HAT-BLK"},
1495 {"209.237.224.0", "255.255.224.0", "UNITEDLAYER-1"},
1496 // {"209.237.225.253", "255.255.255.255", "Old new.toad.com"},
1498 // clang-format on
1500 uint32_t addr32;
1501 CHECK_EQ(inet_pton(AF_INET, addr, &addr32), 1)
1502 << "can't interpret as IPv4 address";
1504 for (auto const& network : networks) {
1505 uint32_t net32;
1506 CHECK_EQ(inet_pton(AF_INET, network.net, &net32), 1)
1507 << "can't grok " << network.net;
1508 uint32_t mask32;
1509 CHECK_EQ(inet_pton(AF_INET, network.mask, &mask32), 1)
1510 << "can't grok " << network.mask;
1512 // sanity check: all unmasked bits must be zero
1513 CHECK_EQ(net32 & (~mask32), 0)
1514 << "bogus config net=" << network.net << ", mask=" << network.mask;
1516 if (net32 == (addr32 & mask32)) {
1517 LOG(INFO) << addr << " allowed " << network.comment;
1518 return true;
1522 return false;
1524 } // namespace
1526 /////////////////////////////////////////////////////////////////////////////
1528 // All of the verify_* functions send their own error messages back to
1529 // the client on failure, and return false.
1531 bool Session::verify_ip_address_(std::string& error_msg)
1533 auto ip_block_db_name = config_path_ / "ip-block";
1534 CDB ip_block;
1535 if (ip_block.open(ip_block_db_name) &&
1536 ip_block.contains(sock_.them_c_str())) {
1537 error_msg =
1538 fmt::format("IP address {} on static blocklist", sock_.them_c_str());
1539 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1540 return false;
1543 client_fcrdns_.clear();
1545 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1546 (sock_.them_address_literal() == IP6::loopback_literal)) {
1547 LOG(INFO) << "loopback address allowed";
1548 ip_allowed_ = true;
1549 client_fcrdns_.emplace_back("localhost");
1550 client_ = fmt::format("localhost {}", sock_.them_address_literal());
1551 return true;
1554 if (IP::is_private(sock_.them_address_literal())) {
1555 LOG(INFO) << "local address allowed";
1556 ip_allowed_ = true;
1557 client_ = sock_.them_address_literal();
1558 return true;
1561 auto const fcrdns = DNS::fcrdns(res_, sock_.them_c_str());
1562 for (auto const& fcr : fcrdns) {
1563 client_fcrdns_.emplace_back(fcr);
1566 if (!client_fcrdns_.empty()) {
1567 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1568 sock_.them_address_literal());
1569 // check allow list
1570 for (auto const& client_fcrdns : client_fcrdns_) {
1571 if (allow_.contains(client_fcrdns.ascii())) {
1572 // LOG(INFO) << "FCrDNS " << client_fcrdns << " allowed";
1573 fcrdns_allowed_ = true;
1574 return true;
1576 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1577 if (tld) {
1578 if (allow_.contains(tld)) {
1579 // LOG(INFO) << "FCrDNS registered domain " << tld << " allowed";
1580 fcrdns_allowed_ = true;
1581 return true;
1585 // check blocklist
1586 for (auto const& client_fcrdns : client_fcrdns_) {
1587 if (block_.contains(client_fcrdns.ascii())) {
1588 error_msg =
1589 fmt::format("FCrDNS {} on static blocklist", client_fcrdns.ascii());
1590 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1591 return false;
1594 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1595 if (tld) {
1596 if (block_.contains(tld)) {
1597 error_msg = fmt::format(
1598 "FCrDNS registered domain {} on static blocklist", tld);
1599 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1600 return false;
1605 else {
1606 client_ = fmt::format("{}", sock_.them_address_literal());
1609 if (IP4::is_address(sock_.them_c_str())) {
1611 if (ip4_allowed(sock_.them_c_str())) {
1612 LOG(INFO) << "on internal allow list";
1613 ip_allowed_ = true;
1614 return true;
1617 auto const reversed{IP4::reverse(sock_.them_c_str())};
1620 // Check with allow list.
1621 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1622 random_device_);
1624 for (auto wl : Config::wls) {
1625 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1626 if (q.has_record()) {
1627 using namespace boost::xpressive;
1629 auto const as = q.get_strings()[0];
1630 LOG(INFO) << "on allow list " << wl << " as " << as;
1632 mark_tag x_(1);
1633 mark_tag y_(2);
1634 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1635 smatch what;
1637 if (regex_match(as, what, rex)) {
1638 auto const x = what[x_].str();
1639 auto const y = what[y_].str();
1641 int value = 0;
1642 std::from_chars(y.data(), y.data() + y.size(), value);
1643 if (value > 0) {
1644 ip_allowed_ = true;
1645 LOG(INFO) << "allowed";
1649 // Any A record skips check on block list
1650 return true;
1655 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1656 std::shuffle(std::begin(Config::bls), std::end(Config::bls),
1657 random_device_);
1659 for (auto bl : Config::bls) {
1661 DNS::Query q(res_, DNS::RR_type::A, reversed + bl);
1662 if (q.has_record()) {
1663 auto const as = q.get_strings()[0];
1664 if (as == "127.0.1.1") {
1665 LOG(INFO) << "Query blocked by " << bl;
1667 else {
1668 error_msg = fmt::format("blocked on advice from {}", bl);
1669 LOG(INFO) << sock_.them_c_str() << " " << error_msg;
1670 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1671 return false;
1675 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1678 return true;
1681 // check the identity from HELO/EHLO
1682 bool Session::verify_client_(Domain const& client_identity,
1683 std::string& error_msg)
1685 if (!client_fcrdns_.empty()) {
1686 if (auto id = std::find(begin(client_fcrdns_), end(client_fcrdns_),
1687 client_identity);
1688 id != end(client_fcrdns_)) {
1689 // If the HELO ident is one of the FCrDNS names...
1690 if (id != begin(client_fcrdns_)) {
1691 // ...then rotate that one to the front of the list
1692 std::rotate(begin(client_fcrdns_), id, id + 1);
1694 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1695 sock_.them_address_literal());
1696 return true;
1698 LOG(INFO) << "claimed identity " << client_identity
1699 << " does NOT match any FCrDNS: ";
1700 for (auto const& client_fcrdns : client_fcrdns_) {
1701 LOG(INFO) << " " << client_fcrdns;
1705 // Bogus clients claim to be us or some local host.
1706 if (sock_.has_peername() && ((client_identity == server_identity_) ||
1707 (client_identity == "localhost") ||
1708 (client_identity == "localhost.localdomain"))) {
1710 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1711 (sock_.them_address_literal() == IP6::loopback_literal)) {
1712 return true;
1715 // Give 'em a pass.
1716 if (ip_allowed_) {
1717 LOG(INFO) << "allow-listed IP address can claim to be "
1718 << client_identity;
1719 return true;
1722 // Ease up in test mode.
1723 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1724 return true;
1727 error_msg = fmt::format("liar, claimed to be {}", client_identity.ascii());
1728 out_() << "550 5.7.1 liar\r\n" << std::flush;
1729 return false;
1732 std::vector<std::string> labels;
1733 boost::algorithm::split(labels, client_identity.ascii(),
1734 boost::algorithm::is_any_of("."));
1735 if (labels.size() < 2) {
1736 error_msg =
1737 fmt::format("claimed bogus identity {}", client_identity.ascii());
1738 out_() << "550 4.7.1 bogus identity\r\n" << std::flush;
1739 return false;
1740 // // Sometimes we may want to look at mail from non conforming
1741 // // sending systems.
1742 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1743 // << client_ << " claiming " << client_identity;
1744 // return true;
1747 if (lookup_domain(block_, client_identity)) {
1748 error_msg =
1749 fmt::format("claimed blocked identity {}", client_identity.ascii());
1750 out_() << "550 4.7.1 blocked identity\r\n" << std::flush;
1751 return false;
1754 auto const tld{tld_db_.get_registered_domain(client_identity.ascii())};
1755 if (!tld) {
1756 // Sometimes we may want to look at mail from misconfigured
1757 // sending systems.
1758 // LOG(WARNING) << "claimed identity has no registered domain";
1759 // return true;
1761 else if (block_.contains(tld)) {
1762 error_msg =
1763 fmt::format("claimed identity has blocked registered domain {}", tld);
1764 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush;
1765 return false;
1768 // not otherwise objectionable
1769 return true;
1772 // check sender from RFC5321 MAIL FROM:
1773 bool Session::verify_sender_(Mailbox const& sender, std::string& error_msg)
1775 std::string const sender_str{sender};
1777 auto bad_senders_db_name = config_path_ / "bad_senders";
1778 CDB bad_senders;
1779 if (bad_senders.open(bad_senders_db_name) &&
1780 bad_senders.contains(sender_str)) {
1781 error_msg = fmt::format("{} bad sender", sender_str);
1782 out_() << "550 5.1.8 " << error_msg << "\r\n" << std::flush;
1783 return false;
1786 // We don't accept mail /from/ a domain we are expecting to accept
1787 // mail for on an external network connection.
1789 if (sock_.them_address_literal() != sock_.us_address_literal()) {
1790 if ((accept_domains_.is_open() &&
1791 (accept_domains_.contains(sender.domain().ascii()) ||
1792 accept_domains_.contains(sender.domain().utf8()))) ||
1793 (sender.domain() == server_identity_)) {
1795 // Ease up in test mode.
1796 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1797 return true;
1799 out_() << "550 5.7.1 liar\r\n" << std::flush;
1800 error_msg = fmt::format("liar, claimed to be {}", sender.domain());
1801 return false;
1805 if (sender.domain().is_address_literal()) {
1806 if (sender.domain() != sock_.them_address_literal()) {
1807 LOG(WARNING) << "sender domain " << sender.domain() << " does not match "
1808 << sock_.them_address_literal();
1810 return true;
1813 do_spf_check_(sender);
1815 if (!verify_sender_domain_(sender.domain(), error_msg)) {
1816 return false;
1819 return true;
1822 // this sender is the RFC5321 MAIL FROM: domain part
1823 bool Session::verify_sender_domain_(Domain const& sender,
1824 std::string& error_msg)
1826 if (sender.empty()) {
1827 // MAIL FROM:<>
1828 // is used to send bounce messages.
1829 return true;
1832 // Break sender domain into labels:
1834 std::vector<std::string> labels;
1835 boost::algorithm::split(labels, sender.ascii(),
1836 boost::algorithm::is_any_of("."));
1838 if (labels.size() < 2) { // This is not a valid domain.
1839 error_msg = fmt::format("{} invalid syntax", sender.ascii());
1840 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1841 return false;
1844 if (lookup_domain(block_, sender)) {
1845 error_msg =
1846 fmt::format("SPF sender domain ({}) is blocked", spf_sender_domain_);
1847 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1848 return false;
1851 if (spf_result_ == SPF::Result::PASS) {
1852 if (allow_.contains(spf_sender_domain_.ascii())) {
1853 LOG(INFO) << "sender " << spf_sender_domain_.ascii() << " allowed";
1854 return true;
1857 auto const reg_dom{
1858 tld_db_.get_registered_domain(spf_sender_domain_.ascii())};
1859 if (reg_dom) {
1860 if (allow_.contains(reg_dom)) {
1861 LOG(INFO) << "sender registered domain \"" << reg_dom << "\" allowed";
1862 return true;
1867 LOG(INFO) << "sender \"" << sender << "\" not disallowed";
1868 return true;
1871 void Session::do_spf_check_(Mailbox const& sender)
1873 if (!sock_.has_peername()) {
1874 auto const ip_addr = "127.0.0.1"; // use localhost for local socket
1875 spf_received_ =
1876 fmt::format("Received-SPF: pass ({}: allow-listed) client-ip={}; "
1877 "envelope-from={}; helo={};",
1878 server_id_(), ip_addr, sender, client_identity_);
1879 spf_sender_domain_ = "localhost";
1880 return;
1883 auto const spf_srv = SPF::Server{server_id_().c_str()};
1884 auto spf_request = SPF::Request{spf_srv};
1886 if (IP4::is_address(sock_.them_c_str())) {
1887 spf_request.set_ipv4_str(sock_.them_c_str());
1889 else if (IP6::is_address(sock_.them_c_str())) {
1890 spf_request.set_ipv6_str(sock_.them_c_str());
1892 else {
1893 LOG(FATAL) << "bogus address " << sock_.them_address_literal() << ", "
1894 << sock_.them_c_str();
1897 auto const from{static_cast<std::string>(sender)};
1899 spf_request.set_env_from(from.c_str());
1900 spf_request.set_helo_dom(client_identity_.ascii().c_str());
1902 auto const spf_res{SPF::Response{spf_request}};
1903 spf_result_ = spf_res.result();
1904 spf_received_ = spf_res.received_spf();
1905 spf_sender_domain_ = spf_request.get_sender_dom();
1907 LOG(INFO) << "spf_received_ == " << spf_received_;
1909 if (spf_result_ == SPF::Result::FAIL) {
1910 LOG(WARNING) << spf_res.header_comment();
1912 else {
1913 LOG(INFO) << spf_res.header_comment();
1917 bool Session::verify_from_params_(parameters_t const& parameters)
1919 // Take a look at the optional parameters:
1920 for (auto const& [name, value] : parameters) {
1921 if (iequal(name, "BODY")) {
1922 if (iequal(value, "8BITMIME")) {
1923 // everything is cool, this is our default...
1925 else if (iequal(value, "7BIT")) {
1926 // nothing to see here, move along...
1928 else if (iequal(value, "BINARYMIME")) {
1929 binarymime_ = true;
1931 else {
1932 LOG(WARNING) << "unrecognized BODY type \"" << value << "\" requested";
1935 else if (iequal(name, "SMTPUTF8")) {
1936 if (!value.empty()) {
1937 LOG(WARNING) << "SMTPUTF8 parameter has a value: " << value;
1939 smtputf8_ = true;
1942 // else if (iequal(name, "PRDR")) {
1943 // LOG(INFO) << "using PRDR";
1944 // prdr_ = true;
1945 // }
1947 else if (iequal(name, "SIZE")) {
1948 if (value.empty()) {
1949 LOG(WARNING) << "SIZE parameter has no value.";
1951 else {
1952 try {
1953 auto const sz = stoull(value);
1954 if (sz > max_msg_size()) {
1955 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1956 LOG(WARNING) << "SIZE parameter too large: " << sz;
1957 return false;
1960 catch (std::invalid_argument const& e) {
1961 LOG(WARNING) << "SIZE parameter has invalid value: " << value;
1963 catch (std::out_of_range const& e) {
1964 LOG(WARNING) << "SIZE parameter has out-of-range value: " << value;
1966 // I guess we just ignore bad size parameters.
1969 else if (iequal(name, "REQUIRETLS")) {
1970 if (!sock_.tls()) {
1971 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush;
1972 LOG(WARNING) << "REQUIRETLS needed";
1973 return false;
1976 else {
1977 LOG(WARNING) << "unrecognized 'MAIL FROM' parameter " << name << "="
1978 << value;
1982 return true;
1985 bool Session::verify_rcpt_params_(parameters_t const& parameters)
1987 // Take a look at the optional parameters:
1988 for (auto const& [name, value] : parameters) {
1989 if (iequal(name, "RRVS")) {
1990 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1991 LOG(INFO) << name << "=" << value;
1993 else {
1994 LOG(WARNING) << "unrecognized 'RCPT TO' parameter " << name << "="
1995 << value;
1999 return true;
2002 // check recipient from RFC5321 RCPT TO:
2003 bool Session::verify_recipient_(Mailbox const& recipient)
2005 if ((recipient.local_part() == "Postmaster") && (recipient.domain() == "")) {
2006 LOG(INFO) << "magic Postmaster address";
2007 return true;
2010 auto const accepted_domain{[this, &recipient] {
2011 if (recipient.domain().is_address_literal()) {
2012 if (recipient.domain() != sock_.us_address_literal()) {
2013 LOG(WARNING) << "recipient.domain address " << recipient.domain()
2014 << " does not match ours " << sock_.us_address_literal();
2015 return false;
2017 return true;
2020 // Domains we accept mail for.
2021 if (accept_domains_.is_open()) {
2022 if (accept_domains_.contains(recipient.domain().ascii()) ||
2023 accept_domains_.contains(recipient.domain().utf8())) {
2024 return true;
2027 else {
2028 // If we have no list of domains to accept, at least take our own.
2029 if (recipient.domain() == server_id_()) {
2030 return true;
2034 return false;
2035 }()};
2037 if (!accepted_domain) {
2038 out_() << "554 5.7.1 relay access denied\r\n" << std::flush;
2039 LOG(WARNING) << "relay access denied for domain " << recipient.domain();
2040 return false;
2043 // Check for local addresses we reject.
2045 auto bad_recipients_db_name = config_path_ / "bad_recipients";
2046 CDB bad_recipients_db;
2047 if (bad_recipients_db.open(bad_recipients_db_name) &&
2048 bad_recipients_db.contains(recipient.local_part())) {
2049 out_() << "550 5.1.1 bad recipient " << recipient << "\r\n" << std::flush;
2050 LOG(WARNING) << "bad recipient " << recipient;
2051 return false;
2056 auto fail_db_name = config_path_ / "fail_554";
2057 CDB fail_db;
2058 if (fail_db.open(fail_db_name) &&
2059 fail_db.contains(recipient.local_part())) {
2060 out_() << "554 5.7.1 prohibited for policy reasons" << recipient << "\r\n"
2061 << std::flush;
2062 LOG(WARNING) << "fail_554 recipient " << recipient;
2063 return false;
2068 auto temp_fail_db_name = config_path_ / "temp_fail";
2069 CDB temp_fail; // Addresses we make wait...
2070 if (temp_fail.open(temp_fail_db_name) &&
2071 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 // This is a trap for a probe done by some senders to see if we
2098 // accept just any old local-part.
2099 if (!extensions_) {
2100 if (recipient.local_part().length() > 8) {
2101 out_() << "550 5.1.1 unknown recipient " << recipient << "\r\n"
2102 << std::flush;
2103 LOG(WARNING) << "unknown recipient for HELO " << recipient;
2104 return false;
2108 return true;