adjust some return codes
[ghsmtp.git] / Session.cpp
blob5a837d401304bb40621fd31d0e5dd15b5b944768
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{2};
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 (smtputf8_)
624 return sock_.tls() ? "UTF8SMTPS" : "UTF8SMTP";
625 else if (extensions_)
626 return sock_.tls() ? "ESMTPS" : "ESMTP";
627 else
628 return sock_.tls() ? "SMTPS" : "SMTP";
629 }()};
631 fmt::memory_buffer headers;
633 // Received-SPF:
634 if (!spf_received_.empty()) {
635 fmt::format_to(headers, "{}\r\n", spf_received_);
638 // Received:
639 // <https://tools.ietf.org/html/rfc5321#section-4.4>
640 fmt::format_to(headers, "Received: from {}", client_identity_.utf8());
641 if (sock_.has_peername()) {
642 fmt::format_to(headers, " ({})", client_);
644 fmt::format_to(headers, "\r\n\tby {} with {} id {}", server_identity_.utf8(),
645 protocol, msg.id());
646 if (forward_path_.size()) {
647 fmt::format_to(headers, "\r\n\tfor <{}>", forward_path_[0]);
648 for (auto i = 1u; i < forward_path_.size(); ++i)
649 fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
651 std::string const tls_info{sock_.tls_info()};
652 if (tls_info.length()) {
653 fmt::format_to(headers, "\r\n\t({})", tls_info);
655 fmt::format_to(headers, ";\r\n\t{}\r\n", msg.when());
657 return fmt::to_string(headers);
660 namespace {
661 bool lookup_domain(CDB& cdb, Domain const& domain)
663 if (!domain.empty()) {
664 if (cdb.contains(domain.ascii())) {
665 return true;
667 if (domain.is_unicode() && cdb.contains(domain.utf8())) {
668 return true;
671 return false;
673 } // namespace
675 std::tuple<Session::SpamStatus, std::string> Session::spam_status_()
677 if (spf_result_ == SPF::Result::FAIL && !ip_allowed_)
678 return {SpamStatus::spam, "SPF failed"};
680 // These should have already been rejected by verify_client_().
681 if ((reverse_path_.domain() == "localhost.local") ||
682 (reverse_path_.domain() == "localhost"))
683 return {SpamStatus::spam, "bogus reverse_path"};
685 std::vector<std::string> why_ham;
687 // Anything enciphered tastes a lot like ham.
688 if (sock_.tls())
689 why_ham.emplace_back("they used TLS");
691 if (spf_result_ == SPF::Result::PASS) {
692 if (lookup_domain(allow_, spf_sender_domain_)) {
693 why_ham.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
694 spf_sender_domain_.utf8()));
696 else {
697 auto tld_dom{tld_db_.get_registered_domain(spf_sender_domain_.ascii())};
698 if (tld_dom && allow_.contains(tld_dom)) {
699 why_ham.emplace_back(fmt::format(
700 "SPF sender registered domain ({}) is allowed", tld_dom));
705 if (fcrdns_allowed_)
706 why_ham.emplace_back(
707 fmt::format("FCrDNS (or it's registered domain) is allowed"));
709 if (!why_ham.empty())
710 return {SpamStatus::ham,
711 fmt::format("{}", fmt::join(std::begin(why_ham), std::end(why_ham),
712 ", and "))};
714 return {SpamStatus::spam, "it's not ham"};
717 static std::string folder(Session::SpamStatus status,
718 std::vector<Mailbox> const& forward_path,
719 Mailbox const& reverse_path)
721 if (status == Session::SpamStatus::spam)
722 return ".Junk";
724 return "";
727 bool Session::msg_new()
729 CHECK((state_ == xact_step::data) || (state_ == xact_step::bdat));
731 auto const& [status, reason]{spam_status_()};
733 LOG(INFO) << ((status == SpamStatus::ham) ? "ham since " : "spam since ")
734 << reason;
736 // All sources of ham get a fresh 5 minute timeout per message.
737 if (status == SpamStatus::ham) {
738 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr))
739 alarm(5 * 60);
742 msg_ = std::make_unique<MessageStore>();
744 if (!FLAGS_max_write)
745 FLAGS_max_write = max_msg_size();
747 try {
748 msg_->open(server_id_(), FLAGS_max_write,
749 folder(status, forward_path_, reverse_path_));
750 auto const hdrs{added_headers_(*(msg_.get()))};
751 msg_->write(hdrs);
753 // fmt::memory_buffer spam_status;
754 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
755 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
756 // msg_->write(spam_status.data(), spam_status.size());
758 LOG(INFO) << "Spam-Status: "
759 << ((status == SpamStatus::spam) ? "Yes" : "No") << ", "
760 << reason;
762 return true;
764 catch (std::system_error const& e) {
765 switch (errno) {
766 case ENOSPC:
767 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
768 LOG(ERROR) << "no space";
769 msg_->trash();
770 msg_.reset();
771 return false;
773 default:
774 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
775 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
776 LOG(ERROR) << e.what();
777 msg_->trash();
778 msg_.reset();
779 return false;
782 catch (std::exception const& e) {
783 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
784 LOG(ERROR) << e.what();
785 msg_->trash();
786 msg_.reset();
787 return false;
790 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
791 LOG(ERROR) << "msg_new failed with no exception caught";
792 msg_->trash();
793 msg_.reset();
794 return false;
797 bool Session::msg_write(char const* s, std::streamsize count)
799 if ((state_ != xact_step::data) && (state_ != xact_step::bdat))
800 return false;
802 if (!msg_)
803 return false;
805 try {
806 if (msg_->write(s, count))
807 return true;
809 catch (std::system_error const& e) {
810 switch (errno) {
811 case ENOSPC:
812 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
813 LOG(ERROR) << "no space";
814 msg_->trash();
815 msg_.reset();
816 return false;
818 default:
819 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
820 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
821 LOG(ERROR) << e.what();
822 msg_->trash();
823 msg_.reset();
824 return false;
827 catch (std::exception const& e) {
828 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
829 LOG(ERROR) << e.what();
830 msg_->trash();
831 msg_.reset();
832 return false;
835 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
836 LOG(ERROR) << "msg_write failed with no exception caught";
837 msg_->trash();
838 msg_.reset();
839 return false;
842 bool Session::data_start()
844 last_in_group_("DATA");
846 switch (state_) {
847 case xact_step::helo:
848 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
849 LOG(WARNING) << "'DATA' before HELO/EHLO"
850 << (sock_.has_peername() ? " from " : "") << client_;
851 return false;
852 case xact_step::mail:
853 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
854 LOG(WARNING) << "'DATA' before 'MAIL FROM'"
855 << (sock_.has_peername() ? " from " : "") << client_;
856 return false;
857 case xact_step::rcpt:
859 /******************************************************************
860 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
862 The DATA command can fail at only two points in the protocol exchange:
864 If there was no MAIL, or no RCPT, command, or all such commands were
865 rejected, the server MAY return a "command out of sequence" (503) or
866 "no valid recipients" (554) reply in response to the DATA command.
868 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
870 The additional restriction is that when there have been no successful
871 RCPT commands in the mail transaction, the DATA command MUST fail
872 with a 503 reply code.
874 Therefore I will send the reply code that is valid for both, and
875 do the same for the BDAT case.
876 *******************************************************************/
878 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
879 LOG(WARNING) << "no valid recipients"
880 << (sock_.has_peername() ? " from " : "") << client_;
881 return false;
882 case xact_step::data: break;
883 case xact_step::bdat:
884 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
885 LOG(WARNING) << "'DATA' during BDAT transfer"
886 << (sock_.has_peername() ? " from " : "") << client_;
887 return false;
888 case xact_step::rset:
889 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
890 LOG(WARNING) << "error state must be cleared with a RSET"
891 << (sock_.has_peername() ? " from " : "") << client_;
892 return false;
895 if (binarymime_) {
896 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
897 << std::flush;
898 LOG(WARNING) << "DATA does not support BINARYMIME";
899 state_ = xact_step::rset; // RFC 3030 section 3 page 5
900 return false;
903 if (!msg_new()) {
904 LOG(ERROR) << "msg_new() failed";
905 return false;
908 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush;
909 LOG(INFO) << "DATA";
910 return true;
913 // bool Session::do_forward_(message::parsed& msg)
914 // {
915 // auto msg_fwd = msg;
917 // // Generate a reply address
918 // Reply::from_to reply;
919 // reply.mail_from = msg_fwd.dmarc_from;
920 // reply.rcpt_to_local_part = fwd_from_.local_part();
922 // auto const reply_addr =
923 // fmt::format("{}@{}", srs_.enc_reply(reply), server_id_());
925 // auto const munging = false;
927 // auto const sender = server_identity_.ascii().c_str();
928 // auto const selector = FLAGS_selector.c_str();
929 // auto const key_file =
930 // (config_path_ / FLAGS_selector).replace_extension("private");
931 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
933 // if (munging) {
934 // auto const from_hdr =
935 // fmt::format("From: \"{} via\" <@>", msg_fwd.dmarc_from, reply_addr);
936 // message::rewrite_from_to(msg_fwd, from_hdr, "", sender, selector,
937 // key_file);
938 // }
939 // else {
940 // auto const reply_to_hdr = fmt::format("Reply-To: {}", reply_addr);
941 // message::rewrite_from_to(msg_fwd, "", reply_to_hdr, sender, selector,
942 // key_file);
943 // }
945 // // Forward it on
946 // if (!send_.send(msg_fwd.as_string())) {
947 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
948 // "stopped\r\n"
949 // << std::flush;
951 // LOG(ERROR) << "failed to send for " << fwd_path_;
952 // return false;
953 // }
955 // LOG(INFO) << "successfully sent for " << fwd_path_;
956 // return true;
957 // }
959 // bool Session::do_reply_(message::parsed& msg)
960 // {
961 // Mailbox to_mbx(rep_info_.mail_from);
962 // Mailbox from_mbx(rep_info_.rcpt_to_local_part, server_identity_);
964 // auto reply = std::make_unique<MessageStore>();
965 // reply->open(server_id_(), FLAGS_max_write, ".Drafts");
967 // auto const date{Now{}};
968 // auto const pill{Pill{}};
969 // auto const mid_str =
970 // fmt::format("<{}.{}@{}>", date.sec(), pill, server_identity_);
972 // fmt::memory_buffer bfr;
974 // fmt::format_to(bfr, "From: <{}>\r\n", from_mbx);
975 // fmt::format_to(bfr, "To: <{}>\r\n", to_mbx);
977 // fmt::format_to(bfr, "Date: {}\r\n", date.c_str());
979 // fmt::format_to(bfr, "Message-ID: {}\r\n", mid_str.c_str());
981 // if (!msg.get_header(message::Subject).empty()) {
982 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
983 // msg.get_header(message::Subject));
984 // }
985 // else {
986 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
987 // "Reply to your message");
988 // }
990 // if (!msg.get_header(message::In_Reply_To).empty()) {
991 // fmt::format_to(bfr, "{}: {}\r\n", message::In_Reply_To,
992 // msg.get_header(message::In_Reply_To));
993 // }
995 // if (!msg.get_header(message::MIME_Version).empty() &&
996 // msg.get_header(message::Content_Type).empty()) {
997 // fmt::format_to(bfr, "{}: {}\r\n", message::MIME_Version,
998 // msg.get_header(message::MIME_Version));
999 // fmt::format_to(bfr, "{}: {}\r\n", message::Content_Type,
1000 // msg.get_header(message::Content_Type));
1001 // }
1003 // reply->write(fmt::to_string(bfr));
1005 // if (!msg.body.empty()) {
1006 // reply->write("\r\n");
1007 // reply->write(msg.body);
1008 // }
1010 // auto const msg_data = reply->freeze();
1011 // message::parsed msg_reply;
1012 // CHECK(msg_reply.parse(msg_data));
1014 // auto const sender = server_identity_.ascii().c_str();
1015 // auto const selector = FLAGS_selector.c_str();
1016 // auto const key_file =
1017 // (config_path_ / FLAGS_selector).replace_extension("private");
1018 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1020 // message::dkim_sign(msg_reply, sender, selector, key_file);
1022 // if (!send_.send(msg_reply.as_string())) {
1023 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1024 // "stopped\r\n"
1025 // << std::flush;
1027 // LOG(ERROR) << "send failed for reply to " << to_mbx << " from " <<
1028 // from_mbx; return false;
1029 // }
1031 // LOG(INFO) << "successful reply to " << to_mbx << " from " << from_mbx;
1032 // return true;
1033 // }
1035 bool Session::do_deliver_()
1037 CHECK(msg_);
1039 // auto const sender = server_identity_.ascii().c_str();
1040 // auto const selector = FLAGS_selector.c_str();
1041 // auto const key_file =
1042 // (config_path_ / FLAGS_selector).replace_extension("private");
1043 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1045 try {
1046 // auto const msg_data = msg_->freeze();
1048 // message::parsed msg;
1050 // // Only deal in RFC-5322 Mail Objects.
1051 // bool const message_parsed = msg.parse(msg_data);
1052 // if (message_parsed) {
1054 // // remove any Return-Path
1055 // message::remove_delivery_headers(msg);
1057 // auto const authentic =
1058 // message_parsed &&
1059 // message::authentication(msg, sender, selector, key_file);
1061 // // write a new Return-Path
1062 // msg_->write(fmt::format("Return-Path: <{}>\r\n", reverse_path_));
1064 // for (auto const h : msg.headers) {
1065 // msg_->write(h.as_string());
1066 // msg_->write("\r\n");
1067 // }
1068 // if (!msg.body.empty()) {
1069 // msg_->write("\r\n");
1070 // msg_->write(msg.body);
1071 // }
1073 msg_->deliver();
1075 // if (authentic && !fwd_path_.empty()) {
1076 // if (!do_forward_(msg))
1077 // return false;
1078 // }
1079 // if (authentic && !rep_info_.empty()) {
1080 // if (!do_reply_(msg))
1081 // return false;
1082 // }
1083 // }
1085 msg_->close();
1087 catch (std::system_error const& e) {
1088 switch (errno) {
1089 case ENOSPC:
1090 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
1091 LOG(ERROR) << "no space";
1092 msg_->trash();
1093 reset_();
1094 return false;
1096 default:
1097 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
1098 if (errno)
1099 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
1100 LOG(ERROR) << e.what();
1101 msg_->trash();
1102 reset_();
1103 return false;
1107 return true;
1110 void Session::data_done()
1112 CHECK((state_ == xact_step::data));
1114 if (msg_ && msg_->size_error()) {
1115 data_size_error();
1116 return;
1119 do_deliver_();
1121 // if (prdr_) {
1122 // out_() << "353\r\n";
1123 // for (auto fp : forward_path_) {
1124 // out_() << "250 2.1.5 RCPT TO OK\r\n";
1125 // }
1126 // }
1129 using namespace boost::xpressive;
1131 mark_tag secs_(1);
1132 sregex const rex = icase("wait-data-") >> (secs_ = +_d);
1133 smatch what;
1135 for (auto fp : forward_path_) {
1136 if (regex_match(fp.local_part(), what, rex)) {
1137 auto const str = what[secs_].str();
1138 LOG(INFO) << "waiting at DATA " << str << " seconds";
1139 long value = 0;
1140 std::from_chars(str.data(), str.data() + str.size(), value);
1141 sleep(value);
1142 LOG(INFO) << "done waiting";
1147 out_() << "250 2.0.0 DATA OK\r\n" << std::flush;
1148 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1149 << msg_->id();
1151 reset_();
1154 void Session::data_size_error()
1156 out_().clear(); // clear possible eof from input side
1157 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1158 if (msg_) {
1159 msg_->trash();
1161 LOG(WARNING) << "DATA size error";
1162 reset_();
1165 void Session::data_error()
1167 out_().clear(); // clear possible eof from input side
1168 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush;
1169 if (msg_) {
1170 msg_->trash();
1172 LOG(WARNING) << "DATA error";
1173 reset_();
1176 bool Session::bdat_start(size_t n)
1178 // In practice, this one gets pipelined.
1179 // last_in_group_("BDAT");
1181 switch (state_) {
1182 case xact_step::helo:
1183 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
1184 LOG(WARNING) << "'BDAT' before HELO/EHLO"
1185 << (sock_.has_peername() ? " from " : "") << client_;
1186 return false;
1187 case xact_step::mail:
1188 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
1189 LOG(WARNING) << "'BDAT' before 'MAIL FROM'"
1190 << (sock_.has_peername() ? " from " : "") << client_;
1191 return false;
1192 case xact_step::rcpt:
1193 // See comment in data_start()
1194 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
1195 LOG(WARNING) << "no valid recipients"
1196 << (sock_.has_peername() ? " from " : "") << client_;
1197 return false;
1198 case xact_step::data: // first bdat
1199 break;
1200 case xact_step::bdat: return true;
1201 case xact_step::rset:
1202 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
1203 LOG(WARNING) << "error state must be cleared with a RSET"
1204 << (sock_.has_peername() ? " from " : "") << client_;
1205 return false;
1208 state_ = xact_step::bdat;
1210 return msg_new();
1213 void Session::bdat_done(size_t n, bool last)
1215 if (state_ != xact_step::bdat) {
1216 bdat_seq_error();
1217 return;
1220 if (!msg_) {
1221 return;
1224 if (msg_->size_error()) {
1225 bdat_size_error();
1226 return;
1229 if (!last) {
1230 out_() << "250 2.0.0 BDAT " << n << " OK\r\n" << std::flush;
1231 LOG(INFO) << "BDAT " << n;
1232 return;
1235 do_deliver_();
1237 out_() << "250 2.0.0 BDAT " << n << " LAST OK\r\n" << std::flush;
1239 LOG(INFO) << "BDAT " << n << " LAST";
1240 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1241 << msg_->id();
1242 reset_();
1245 void Session::bdat_size_error()
1247 out_().clear(); // clear possible eof from input side
1248 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1249 if (msg_) {
1250 msg_->trash();
1252 LOG(WARNING) << "BDAT size error";
1253 reset_();
1256 void Session::bdat_seq_error()
1258 out_().clear(); // clear possible eof from input side
1259 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush;
1260 if (msg_) {
1261 msg_->trash();
1263 LOG(WARNING) << "BDAT sequence error";
1264 reset_();
1267 void Session::bdat_io_error()
1269 out_().clear(); // clear possible eof from input side
1270 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush;
1271 if (msg_) {
1272 msg_->trash();
1274 LOG(WARNING) << "BDAT I/O error";
1275 reset_();
1278 void Session::rset()
1280 out_() << "250 2.1.5 RSET OK\r\n";
1281 // No flush RFC-2920 section 3.1, this could be part of a command group.
1282 LOG(INFO) << "RSET";
1283 reset_();
1286 void Session::noop(std::string_view str)
1288 last_in_group_("NOOP");
1289 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush;
1290 LOG(INFO) << "NOOP" << (str.length() ? " " : "") << str;
1293 void Session::vrfy(std::string_view str)
1295 last_in_group_("VRFY");
1296 out_() << "252 2.1.5 try it\r\n" << std::flush;
1297 LOG(INFO) << "VRFY" << (str.length() ? " " : "") << str;
1300 void Session::help(std::string_view str)
1302 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n" << std::flush;
1303 LOG(INFO) << "HELP" << (str.length() ? " " : "") << str;
1306 void Session::quit()
1308 // send_.quit();
1309 // last_in_group_("QUIT");
1310 out_() << "221 2.0.0 closing connection\r\n" << std::flush;
1311 LOG(INFO) << "QUIT";
1312 exit_();
1315 void Session::auth()
1317 out_() << "454 4.7.0 authentication failure\r\n" << std::flush;
1318 LOG(INFO) << "AUTH";
1319 bad_host_("auth");
1322 void Session::error(std::string_view log_msg)
1324 out_() << "421 4.3.5 system error: " << log_msg << "\r\n" << std::flush;
1325 LOG(WARNING) << log_msg;
1328 void Session::cmd_unrecognized(std::string_view cmd)
1330 auto const escaped{esc(cmd)};
1331 LOG(WARNING) << "command unrecognized: \"" << escaped << "\"";
1333 if (++n_unrecognized_cmds_ >= Config::max_unrecognized_cmds) {
1334 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1335 << "\" exceeds limit\r\n"
1336 << std::flush;
1337 LOG(WARNING) << n_unrecognized_cmds_
1338 << " unrecognized commands is too many";
1339 exit_();
1342 out_() << "500 5.5.1 command unrecognized: \"" << escaped << "\"\r\n"
1343 << std::flush;
1346 void Session::bare_lf()
1348 // Error code used by Office 365.
1349 out_() << "554 5.6.11 bare LF\r\n" << std::flush;
1350 LOG(WARNING) << "bare LF";
1351 exit_();
1354 void Session::max_out()
1356 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1357 LOG(WARNING) << "message size maxed out";
1358 exit_();
1361 void Session::time_out()
1363 out_() << "421 4.4.2 time-out\r\n" << std::flush;
1364 LOG(WARNING) << "time-out" << (sock_.has_peername() ? " from " : "")
1365 << client_;
1366 exit_();
1369 void Session::starttls()
1371 last_in_group_("STARTTLS");
1372 if (sock_.tls()) {
1373 out_() << "554 5.5.1 TLS already active\r\n" << std::flush;
1374 LOG(WARNING) << "STARTTLS issued with TLS already active";
1376 else {
1377 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush;
1378 if (sock_.starttls_server(config_path_)) {
1379 reset_();
1380 max_msg_size(Config::max_msg_size_bro);
1381 LOG(INFO) << "STARTTLS " << sock_.tls_info();
1386 void Session::exit_()
1388 // sock_.log_totals();
1390 timespec time_used{};
1391 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time_used);
1393 LOG(INFO) << "CPU time " << time_used.tv_sec << "." << std::setw(9)
1394 << std::setfill('0') << time_used.tv_nsec << " seconds";
1396 std::exit(EXIT_SUCCESS);
1399 namespace {
1400 bool ip4_allowed(char const* addr)
1402 struct nw {
1403 char const* net;
1404 char const* mask;
1405 char const* comment;
1408 // clang-format off
1410 // 255 0b11111111 8
1411 // 254 0b11111110 7
1412 // 252 0b11111100 6
1413 // 248 0b11111000 5
1414 // 240 0b11110000 4
1415 // 224 0b11100000 3
1416 // 192 0b11000000 2
1417 // 128 0b10000000 1
1419 nw const networks[]{
1420 // the one very special case
1421 {"108.83.36.112", "255.255.255.248", "108.83.36.112/29"},
1423 // accept from major providers:
1424 {"3.0.0.0", "255.0.0.0", "3.0.0.0/9 and 3.128.0.0/9 Amazon"},
1425 {"5.45.198.0", "255.255.254.0", "5.45.198.0/23 YANDEX-5-45-198"},
1426 {"12.153.224.0", "255.255.255.0", "12.153.224.0/24 E-TRADE10-224"},
1427 {"17.0.0.0", "255.0.0.0", "17.0.0.0/8 APPLE-WWNET"},
1428 {"40.74.0.0", "255.254.0.0", "40.74.0.0/15 MSFT NET-40-74-0-0-1"},
1429 {"40.76.0.0", "255.252.0.0", "40.76.0.0/14 MSFT NET-40-74-0-0-1"},
1430 {"40.80.0.0", "255.240.0.0", "40.80.0.0/12 MSFT NET-40-74-0-0-1"},
1431 {"40.92.0.0", "255.252.0.0", "40.96.0.0/14 MSFT NET-40-74-0-0-1"},
1432 {"40.96.0.0", "255.240.0.0", "40.96.0.0/12 MSFT NET-40-74-0-0-1"},
1433 {"40.112.0.0", "255.248.0.0", "40.112.0.0/13 MSFT NET-40-74-0-0-1"},
1434 {"40.120.0.0", "255.252.0.0", "40.120.0.0/14 MSFT NET-40-74-0-0-1"},
1435 {"40.124.0.0", "255.255.0.0", "40.124.0.0/16 MSFT NET-40-74-0-0-1"},
1436 {"40.125.0.0", "255.255.128.0", "40.125.0.0/17 MSFT NET-40-74-0-0-1"},
1437 {"56.0.0.0", "255.0.0.0", "56.0.0.0/8 USPS1"},
1438 {"65.52.0.0", "255.252.0.0", "65.52.0.0/14 MICROSOFT-1BLK"},
1439 {"66.163.160.0", "255.255.224.0", "66.163.160.0/19 A-YAHOO-US2"},
1440 {"66.211.176.0", "255.255.240.0", "66.211.176.0/20 EBAY-2"},
1441 {"66.211.172.0", "255.255.252.0", "66.211.172.0/22 EBAY-2"},
1442 {"66.220.144.0", "255.255.240.0", "66.220.144.0/20 TFBNET3"},
1443 {"68.232.192.0", "255.255.240.0", "68.232.192.0/20 EXACT-IP-NET-2"},
1444 {"69.171.224.0", "255.255.224.0", "69.171.224.0/19 TFBNET3"},
1445 {"70.47.67.0", "255.255.255.0", "70.47.67.0/24 NET-462F4300-24"},
1446 {"74.6.0.0", "255.255.0.0", "INKTOMI-BLK-6 Oath"},
1447 {"74.125.0.0", "255.255.0.0", "74.125.0.0/16 GOOGLE"},
1448 {"75.101.100.43", "255.255.255.255", "new.toad.com"},
1449 {"76.178.68.57", "255.255.255.255", "cpe-76-178-68-57.natsow.res.rr.com"},
1450 {"98.136.0.0", "255.252.0.0", "98.136.0.0/14 A-YAHOO-US9"},
1451 {"104.40.0.0", "255.248.0.0", "104.40.0.0/13 MSFT"},
1452 {"108.174.0.0", "255.255.240.0", "108.174.0.0/20 LINKEDIN"},
1453 {"159.45.0.0", "255.255.0.0", "159.45.0.0/16 AGE-COM"},
1454 {"159.53.0.0", "255.255.0.0", "159.53.0.0/16 JMC"},
1455 {"159.135.224.0", "255.255.240.0", "159.135.224.0/20 MNO87-159-135-224-0-0"},
1456 {"162.247.72.0", "255.255.252.0", "162.247.72.0/22 CALYX-INSTITUTE-V4-1"},
1457 {"165.107.0.0", "255.255.0.0", "NET-LDC-CA-GOV"},
1458 {"192.175.128.0", "255.255.128.0", "192.175.128.0/17 NETBLK-VANGUARD"},
1459 {"198.2.128.0", "255.255.192.0", "198.2.128.0/18 RSG-DELIVERY"},
1460 {"198.252.206.0", "255.255.255.0", "198.252.206.0/24 SE-NET01"},
1461 {"199.122.120.0", "255.255.248.0", "199.122.120.0/21 EXACT-IP-NET-3"},
1462 {"204.13.164.0", "255.255.255.0", "204.13.164.0/24 RISEUP-NETWORKS-SWIFT-BLOCK2"},
1463 {"204.29.186.0", "255.255.254.0", "204.29.186.0/23 ATDN-NSCAPE"},
1464 {"205.139.104.0", "255.255.252.0", "205.139.104.0/22 SAVV-S259964-8"},
1465 {"205.201.128.0", "255.255.240.0", "205.201.128.0/20 RSG-DELIVERY"},
1466 {"208.118.235.0", "255.255.255.0", "208.118.235.0/24 TWDX-208-118-235-0-1"},
1467 {"208.192.0.0", "255.192.0.0", "208.192.0.0/10 UUNET1996B"},
1468 {"209.51.188.0", "255.255.255.0", "I:NET-209.51.188.0/24 FSF"},
1469 {"209.85.128.0", "255.255.128.0", "209.85.128.0/17 GOOGLE"},
1470 {"209.132.176.0", "255.255.240.0", "209.132.176.0/20 RED-HAT-BLK"},
1471 {"209.237.224.0", "255.255.224.0", "UNITEDLAYER-1"},
1472 // {"209.237.225.253", "255.255.255.255", "Old new.toad.com"},
1474 // clang-format on
1476 uint32_t addr32;
1477 CHECK_EQ(inet_pton(AF_INET, addr, &addr32), 1)
1478 << "can't interpret as IPv4 address";
1480 for (auto const& network : networks) {
1481 uint32_t net32;
1482 CHECK_EQ(inet_pton(AF_INET, network.net, &net32), 1)
1483 << "can't grok " << network.net;
1484 uint32_t mask32;
1485 CHECK_EQ(inet_pton(AF_INET, network.mask, &mask32), 1)
1486 << "can't grok " << network.mask;
1488 // sanity check: all unmasked bits must be zero
1489 CHECK_EQ(net32 & (~mask32), 0)
1490 << "bogus config net=" << network.net << ", mask=" << network.mask;
1492 if (net32 == (addr32 & mask32)) {
1493 LOG(INFO) << addr << " allowed " << network.comment;
1494 return true;
1498 return false;
1500 } // namespace
1502 /////////////////////////////////////////////////////////////////////////////
1504 // All of the verify_* functions send their own error messages back to
1505 // the client on failure, and return false.
1507 bool Session::verify_ip_address_(std::string& error_msg)
1509 auto ip_block_db_name = config_path_ / "ip-block";
1510 CDB ip_block{ip_block_db_name};
1511 if (ip_block.contains(sock_.them_c_str())) {
1512 error_msg =
1513 fmt::format("IP address {} on static blocklist", sock_.them_c_str());
1514 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1515 return false;
1518 client_fcrdns_.clear();
1520 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1521 (sock_.them_address_literal() == IP6::loopback_literal)) {
1522 LOG(INFO) << "loopback address allowed";
1523 ip_allowed_ = true;
1524 client_fcrdns_.emplace_back("localhost");
1525 client_ = fmt::format("localhost {}", sock_.them_address_literal());
1526 return true;
1529 if (IP::is_private(sock_.them_address_literal())) {
1530 LOG(INFO) << "local address allowed";
1531 ip_allowed_ = true;
1532 client_ = sock_.them_address_literal();
1533 return true;
1536 auto const fcrdns = DNS::fcrdns(res_, sock_.them_c_str());
1537 for (auto const& fcr : fcrdns) {
1538 client_fcrdns_.emplace_back(fcr);
1541 if (!client_fcrdns_.empty()) {
1542 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1543 sock_.them_address_literal());
1544 // check allow list
1545 for (auto const& client_fcrdns : client_fcrdns_) {
1546 if (allow_.contains(client_fcrdns.ascii())) {
1547 // LOG(INFO) << "FCrDNS " << client_fcrdns << " allowed";
1548 fcrdns_allowed_ = true;
1549 return true;
1551 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1552 if (tld) {
1553 if (allow_.contains(tld)) {
1554 // LOG(INFO) << "FCrDNS registered domain " << tld << " allowed";
1555 fcrdns_allowed_ = true;
1556 return true;
1560 // check blocklist
1561 for (auto const& client_fcrdns : client_fcrdns_) {
1562 if (block_.contains(client_fcrdns.ascii())) {
1563 error_msg =
1564 fmt::format("FCrDNS {} on static blocklist", client_fcrdns.ascii());
1565 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1566 return false;
1569 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1570 if (tld) {
1571 if (block_.contains(tld)) {
1572 error_msg = fmt::format(
1573 "FCrDNS registered domain {} on static blocklist", tld);
1574 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1575 return false;
1580 else {
1581 client_ = fmt::format("unknown {}", sock_.them_address_literal());
1584 if (IP4::is_address(sock_.them_c_str())) {
1586 if (ip4_allowed(sock_.them_c_str())) {
1587 LOG(INFO) << "on internal allow list";
1588 ip_allowed_ = true;
1589 return true;
1592 auto const reversed{IP4::reverse(sock_.them_c_str())};
1594 // Check with allow list.
1595 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1596 random_device_);
1598 for (auto wl : Config::wls) {
1599 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1600 if (q.has_record()) {
1601 using namespace boost::xpressive;
1603 auto const as = q.get_strings()[0];
1604 LOG(INFO) << "on allow list " << wl << " as " << as;
1606 mark_tag x_(1);
1607 mark_tag y_(2);
1608 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1609 smatch what;
1611 if (regex_match(as, what, rex)) {
1612 auto const x = what[x_].str();
1613 auto const y = what[y_].str();
1615 int value = 0;
1616 std::from_chars(y.data(), y.data() + y.size(), value);
1617 if (value > 0) {
1618 ip_allowed_ = true;
1619 LOG(INFO) << "allowed";
1623 // Any A record skips check on block list
1624 return true;
1628 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1629 std::shuffle(std::begin(Config::bls), std::end(Config::bls),
1630 random_device_);
1632 for (auto bl : Config::bls) {
1634 DNS::Query q(res_, DNS::RR_type::A, reversed + bl);
1635 if (q.has_record()) {
1636 auto const as = q.get_strings()[0];
1637 if (as == "127.0.1.1") {
1638 LOG(INFO) << "Query blocked by " << bl;
1640 else {
1641 error_msg = fmt::format("blocked on advice from {}", bl);
1642 LOG(INFO) << sock_.them_c_str() << " " << error_msg;
1643 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1644 return false;
1648 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1651 return true;
1654 // check the identity from HELO/EHLO
1655 bool Session::verify_client_(Domain const& client_identity,
1656 std::string& error_msg)
1658 if (!client_fcrdns_.empty()) {
1659 if (auto id = std::find(begin(client_fcrdns_), end(client_fcrdns_),
1660 client_identity);
1661 id != end(client_fcrdns_)) {
1662 // If the HELO ident is one of the FCrDNS names...
1663 if (id != begin(client_fcrdns_)) {
1664 // ...then rotate that one to the front of the list
1665 std::rotate(begin(client_fcrdns_), id, id + 1);
1667 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1668 sock_.them_address_literal());
1669 return true;
1671 LOG(INFO) << "claimed identity " << client_identity
1672 << " does NOT match any FCrDNS: ";
1673 for (auto const& client_fcrdns : client_fcrdns_) {
1674 LOG(INFO) << " " << client_fcrdns;
1678 // Bogus clients claim to be us or some local host.
1679 if (sock_.has_peername() && ((client_identity == server_identity_) ||
1680 (client_identity == "localhost") ||
1681 (client_identity == "localhost.localdomain"))) {
1683 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1684 (sock_.them_address_literal() == IP6::loopback_literal)) {
1685 return true;
1688 // Give 'em a pass.
1689 if (ip_allowed_) {
1690 LOG(INFO) << "allow-listed IP address can claim to be "
1691 << client_identity;
1692 return true;
1695 // Ease up in test mode.
1696 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1697 return true;
1700 error_msg = fmt::format("liar, claimed to be {}", client_identity.ascii());
1701 out_() << "550 5.7.1 liar\r\n" << std::flush;
1702 return false;
1705 std::vector<std::string> labels;
1706 boost::algorithm::split(labels, client_identity.ascii(),
1707 boost::algorithm::is_any_of("."));
1708 if (labels.size() < 2) {
1709 error_msg =
1710 fmt::format("claimed bogus identity {}", client_identity.ascii());
1711 out_() << "550 4.7.1 bogus identity\r\n" << std::flush;
1712 return false;
1713 // // Sometimes we may want to look at mail from non conforming
1714 // // sending systems.
1715 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1716 // << client_ << " claiming " << client_identity;
1717 // return true;
1720 if (lookup_domain(block_, client_identity)) {
1721 error_msg =
1722 fmt::format("claimed blocked identity {}", client_identity.ascii());
1723 out_() << "550 4.7.1 blocked identity\r\n" << std::flush;
1724 return false;
1727 auto const tld{tld_db_.get_registered_domain(client_identity.ascii())};
1728 if (!tld) {
1729 // Sometimes we may want to look at mail from misconfigured
1730 // sending systems.
1731 // LOG(WARNING) << "claimed identity has no registered domain";
1732 // return true;
1734 else if (block_.contains(tld)) {
1735 error_msg =
1736 fmt::format("claimed identity has blocked registered domain {}", tld);
1737 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush;
1738 return false;
1741 // not otherwise objectionable
1742 return true;
1745 // check sender from RFC5321 MAIL FROM:
1746 bool Session::verify_sender_(Mailbox const& sender, std::string& error_msg)
1748 std::string const sender_str{sender};
1750 auto bad_senders_db_name = config_path_ / "bad_senders";
1751 CDB bad_senders{bad_senders_db_name}; // Addresses we don't accept mail from.
1752 if (bad_senders.contains(sender_str)) {
1753 error_msg = fmt::format("{} bad sender", sender_str);
1754 out_() << "550 5.1.8 " << error_msg << "\r\n" << std::flush;
1755 return false;
1758 // We don't accept mail /from/ a domain we are expecting to accept
1759 // mail for on an external network connection.
1761 if (sock_.them_address_literal() != sock_.us_address_literal()) {
1762 if ((accept_domains_.is_open() &&
1763 (accept_domains_.contains(sender.domain().ascii()) ||
1764 accept_domains_.contains(sender.domain().utf8()))) ||
1765 (sender.domain() == server_identity_)) {
1767 // Ease up in test mode.
1768 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1769 return true;
1771 out_() << "550 5.7.1 liar\r\n" << std::flush;
1772 error_msg = fmt::format("liar, claimed to be {}", sender.domain());
1773 return false;
1777 if (sender.domain().is_address_literal()) {
1778 if (sender.domain() != sock_.them_address_literal()) {
1779 LOG(WARNING) << "sender domain " << sender.domain() << " does not match "
1780 << sock_.them_address_literal();
1782 return true;
1785 if (!verify_sender_spf_(sender)) {
1786 error_msg = "failed SPF check";
1787 return false;
1790 if (!verify_sender_domain_(sender.domain(), error_msg)) {
1791 return false;
1794 return true;
1797 // this sender is the RFC5321 MAIL FROM: domain part
1798 bool Session::verify_sender_domain_(Domain const& sender,
1799 std::string& error_msg)
1801 if (sender.empty()) {
1802 // MAIL FROM:<>
1803 // is used to send bounce messages.
1804 return true;
1807 // Break sender domain into labels:
1809 std::vector<std::string> labels;
1810 boost::algorithm::split(labels, sender.ascii(),
1811 boost::algorithm::is_any_of("."));
1813 if (labels.size() < 2) { // This is not a valid domain.
1814 error_msg = fmt::format("{} invalid syntax", sender.ascii());
1815 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1816 return false;
1819 if (allow_.contains(sender.ascii())) {
1820 LOG(INFO) << "sender " << sender.ascii() << " allowed";
1821 return true;
1823 auto const reg_dom{tld_db_.get_registered_domain(sender.ascii())};
1824 if (reg_dom) {
1825 if (allow_.contains(reg_dom)) {
1826 LOG(INFO) << "sender registered domain \"" << reg_dom << "\" allowed";
1827 return true;
1830 // LOG(INFO) << "looking up " << reg_dom;
1831 return verify_sender_domain_uribl_(reg_dom, error_msg);
1834 LOG(INFO) << "sender \"" << sender << "\" not disallowed";
1835 return true;
1838 // check sender domain on dynamic URI block lists
1839 bool Session::verify_sender_domain_uribl_(std::string_view sender,
1840 std::string& error_msg)
1842 if (!sock_.has_peername()) // short circuit
1843 return true;
1845 std::shuffle(std::begin(Config::uribls), std::end(Config::uribls),
1846 random_device_);
1847 for (auto uribl : Config::uribls) {
1848 auto const lookup = fmt::format("{}.{}", sender, uribl);
1849 auto as = DNS::get_strings(res_, DNS::RR_type::A, lookup);
1850 if (!as.empty()) {
1851 if (as.front() == "127.0.0.1")
1852 continue;
1853 error_msg = fmt::format("{} blocked on advice of {}", sender, uribl);
1854 out_() << "550 5.7.1 sender " << error_msg << "\r\n" << std::flush;
1855 return false;
1859 LOG(INFO) << "sender \"" << sender << "\" not blocked by URIBLs";
1860 return true;
1863 bool Session::verify_sender_spf_(Mailbox const& sender)
1865 if (!sock_.has_peername()) {
1866 auto const ip_addr = "127.0.0.1"; // use localhost for local socket
1867 spf_received_ =
1868 fmt::format("Received-SPF: pass ({}: allow-listed) client-ip={}; "
1869 "envelope-from={}; helo={};",
1870 server_id_(), ip_addr, sender, client_identity_);
1871 spf_sender_domain_ = "localhost";
1872 return true;
1875 auto const spf_srv = SPF::Server{server_id_().c_str()};
1876 auto spf_request = SPF::Request{spf_srv};
1878 if (IP4::is_address(sock_.them_c_str())) {
1879 spf_request.set_ipv4_str(sock_.them_c_str());
1881 else if (IP6::is_address(sock_.them_c_str())) {
1882 spf_request.set_ipv6_str(sock_.them_c_str());
1884 else {
1885 LOG(FATAL) << "bogus address " << sock_.them_address_literal() << ", "
1886 << sock_.them_c_str();
1889 auto const from{static_cast<std::string>(sender)};
1891 spf_request.set_env_from(from.c_str());
1892 spf_request.set_helo_dom(client_identity_.ascii().c_str());
1894 auto const spf_res{SPF::Response{spf_request}};
1895 spf_result_ = spf_res.result();
1896 spf_received_ = spf_res.received_spf();
1897 spf_sender_domain_ = spf_request.get_sender_dom();
1899 LOG(INFO) << "spf_received_ == " << spf_received_;
1901 if (spf_result_ == SPF::Result::FAIL) {
1902 LOG(WARNING) << spf_res.header_comment();
1904 else {
1905 LOG(INFO) << spf_res.header_comment();
1908 if (spf_result_ == SPF::Result::PASS) {
1909 if (lookup_domain(block_, spf_sender_domain_)) {
1910 LOG(INFO) << "SPF sender domain (" << spf_sender_domain_
1911 << ") is blocked";
1912 return false;
1916 return true;
1919 bool Session::verify_from_params_(parameters_t const& parameters)
1921 // Take a look at the optional parameters:
1922 for (auto const& [name, value] : parameters) {
1923 if (iequal(name, "BODY")) {
1924 if (iequal(value, "8BITMIME")) {
1925 // everything is cool, this is our default...
1927 else if (iequal(value, "7BIT")) {
1928 // nothing to see here, move along...
1930 else if (iequal(value, "BINARYMIME")) {
1931 binarymime_ = true;
1933 else {
1934 LOG(WARNING) << "unrecognized BODY type \"" << value << "\" requested";
1937 else if (iequal(name, "SMTPUTF8")) {
1938 if (!value.empty()) {
1939 LOG(WARNING) << "SMTPUTF8 parameter has a value: " << value;
1941 smtputf8_ = true;
1944 // else if (iequal(name, "PRDR")) {
1945 // LOG(INFO) << "using PRDR";
1946 // prdr_ = true;
1947 // }
1949 else if (iequal(name, "SIZE")) {
1950 if (value.empty()) {
1951 LOG(WARNING) << "SIZE parameter has no value.";
1953 else {
1954 try {
1955 auto const sz = stoull(value);
1956 if (sz > max_msg_size()) {
1957 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1958 LOG(WARNING) << "SIZE parameter too large: " << sz;
1959 return false;
1962 catch (std::invalid_argument const& e) {
1963 LOG(WARNING) << "SIZE parameter has invalid value: " << value;
1965 catch (std::out_of_range const& e) {
1966 LOG(WARNING) << "SIZE parameter has out-of-range value: " << value;
1968 // I guess we just ignore bad size parameters.
1971 else if (iequal(name, "REQUIRETLS")) {
1972 if (!sock_.tls()) {
1973 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush;
1974 LOG(WARNING) << "REQUIRETLS needed";
1975 return false;
1978 else {
1979 LOG(WARNING) << "unrecognized 'MAIL FROM' parameter " << name << "="
1980 << value;
1984 return true;
1987 bool Session::verify_rcpt_params_(parameters_t const& parameters)
1989 // Take a look at the optional parameters:
1990 for (auto const& [name, value] : parameters) {
1991 if (iequal(name, "RRVS")) {
1992 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1993 LOG(INFO) << name << "=" << value;
1995 else {
1996 LOG(WARNING) << "unrecognized 'RCPT TO' parameter " << name << "="
1997 << value;
2001 return true;
2004 // check recipient from RFC5321 RCPT TO:
2005 bool Session::verify_recipient_(Mailbox const& recipient)
2007 if ((recipient.local_part() == "Postmaster") && (recipient.domain() == "")) {
2008 LOG(INFO) << "magic Postmaster address";
2009 return true;
2012 auto const accepted_domain{[this, &recipient] {
2013 if (recipient.domain().is_address_literal()) {
2014 if (recipient.domain() != sock_.us_address_literal()) {
2015 LOG(WARNING) << "recipient.domain address " << recipient.domain()
2016 << " does not match ours " << sock_.us_address_literal();
2017 return false;
2019 return true;
2022 // Domains we accept mail for.
2023 if (accept_domains_.is_open()) {
2024 if (accept_domains_.contains(recipient.domain().ascii()) ||
2025 accept_domains_.contains(recipient.domain().utf8())) {
2026 return true;
2029 else {
2030 // If we have no list of domains to accept, at least take our own.
2031 if (recipient.domain() == server_id_()) {
2032 return true;
2036 return false;
2037 }()};
2039 if (!accepted_domain) {
2040 out_() << "554 5.7.1 relay access denied\r\n" << std::flush;
2041 LOG(WARNING) << "relay access denied for domain " << recipient.domain();
2042 return false;
2045 // Check for local addresses we reject.
2047 auto bad_recipients_db_name = config_path_ / "bad_recipients";
2048 CDB bad_recipients_db{bad_recipients_db_name};
2049 if (bad_recipients_db.contains(recipient.local_part())) {
2050 out_() << "550 5.1.1 bad recipient " << recipient << "\r\n" << std::flush;
2051 LOG(WARNING) << "bad recipient " << recipient;
2052 return false;
2057 auto temp_fail_db_name = config_path_ / "temp_fail";
2058 CDB temp_fail{temp_fail_db_name}; // Addresses we make wait...
2059 if (temp_fail.contains(recipient.local_part())) {
2060 out_() << "432 4.3.0 Recipient's incoming mail queue has been stopped\r\n"
2061 << std::flush;
2062 LOG(WARNING) << "temp fail for recipient " << recipient;
2063 return false;
2067 // Check for and act on magic "wait" address.
2069 using namespace boost::xpressive;
2071 mark_tag secs_(1);
2072 sregex const rex = icase("wait-rcpt-") >> (secs_ = +_d);
2073 smatch what;
2075 if (regex_match(recipient.local_part(), what, rex)) {
2076 auto const str = what[secs_].str();
2077 LOG(INFO) << "waiting at RCPT TO " << str << " seconds";
2078 long value = 0;
2079 std::from_chars(str.data(), str.data() + str.size(), value);
2080 sleep(value);
2081 LOG(INFO) << "done waiting";
2085 if (!extensions_) {
2086 if (recipient.local_part().length() > 8) {
2087 out_() << "550 5.1.1 unknown recipient " << recipient << "\r\n"
2088 << std::flush;
2089 LOG(WARNING) << "unknown recipient for HELO " << recipient;
2090 return false;
2094 return true;