from, not to
[ghsmtp.git] / Session.cpp
bloba2b0ada8408c655a1d905140eae4966688946f70
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 #include <gflags/gflags.h>
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 <https://www.spamhaus.org/faq/section/DNSBL%20Usage>
101 Spamhaus uses this general convention for return codes:
103 Return Code Description
104 127.0.0.0/24 Spamhaus IP Blocklists
105 127.0.1.0/24 Spamhaus Domain Blocklists
106 127.0.2.0/24 Spamhaus Zero Reputation Domains list
107 127.255.255.0/24 ERRORS (not implying a "listed" response)
109 Currently used return codes for Spamhaus public IP zones:
111 Return Code Zone Description
112 127.0.0.2 SBL Spamhaus SBL Data
113 127.0.0.3 SBL Spamhaus SBL CSS Data
114 127.0.0.4 XBL CBL Data
115 127.0.0.9 SBL Spamhaus DROP/EDROP Data
116 (in addition to 127.0.0.2, since 01-Jun-2016)
117 127.0.0.10 PBL ISP Maintained
118 127.0.0.11 PBL Spamhaus Maintained
120 127.0.0.5-7 are allocated to XBL for possible future use;
121 127.0.0.8 is allocated to SBL for possible future use.
123 From <https://www.spamhaus.org/faq/section/Spamhaus%20DBL#291>
125 Return Codes Data Source
126 127.0.1.2 spam domain
127 127.0.1.4 phish domain
128 127.0.1.5 malware domain
129 127.0.1.6 botnet C&C domain
130 127.0.1.102 abused legit spam
131 127.0.1.103 abused spammed redirector domain
132 127.0.1.104 abused legit phish
133 127.0.1.105 abused legit malware
134 127.0.1.106 abused legit botnet C&C
135 127.0.1.255 IP queries prohibited!
137 The following special codes indicate an error condition and should not
138 be taken to imply that the queried domain is "listed":
140 Return Code Description
141 127.255.255.252 Typing error in DNSBL name
142 127.255.255.254 Anonymous query through public resolver
143 127.255.255.255 Excessive number of queries
146 From <http://www.surbl.org/lists#multi>
148 last octet indicates which lists it belongs to. The bit positions in
149 that last octet for membership in the different lists are:
151 8 = listed on PH
152 16 = listed on MW
153 64 = listed on ABUSE
154 128 = listed on CR
158 char const* uribls[]{
159 "dbl.spamhaus.org",
160 "multi.uribl.com",
163 constexpr auto greeting_wait = std::chrono::seconds{6};
164 constexpr int max_recipients_per_message = 100;
165 constexpr int max_unrecognized_cmds = 20;
167 // Read timeout value gleaned from RFC-1123 section 5.3.2 and RFC-5321
168 // section 4.5.3.2.7.
169 constexpr auto read_timeout = std::chrono::minutes{5};
170 constexpr auto write_timeout = std::chrono::seconds{30};
171 } // namespace Config
173 DEFINE_bool(immortal, false, "don't set process timout");
175 DEFINE_uint64(max_read, 0, "max data to read");
176 DEFINE_uint64(max_write, 0, "max data to write");
178 DEFINE_bool(rrvs, false, "support RRVS à la RFC 7293");
180 DEFINE_string(selector, "ghsmtp", "DKIM selector");
182 DEFINE_bool(test_mode, false, "ease up on some checks");
184 DEFINE_bool(use_pipelining, true, "use PIPELINING extension");
186 boost::xpressive::mark_tag secs_(1);
187 boost::xpressive::sregex const all_rex = boost::xpressive::icase("wait-all-") >>
188 (secs_ = +boost::xpressive::_d);
190 Session::Session(fs::path config_path,
191 std::function<void(void)> read_hook,
192 int fd_in,
193 int fd_out)
194 : config_path_(config_path)
195 , res_(config_path)
196 , sock_(fd_in, fd_out, read_hook, Config::read_timeout, Config::write_timeout)
197 //, send_(config_path, "smtp")
198 //, srs_(config_path)
200 auto accept_db_name = config_path_ / "accept_domains";
201 auto allow_db_name = config_path_ / "allow";
202 auto block_db_name = config_path_ / "block";
203 auto forward_db_name = config_path_ / "forward";
205 accept_domains_.open(accept_db_name);
206 allow_.open(allow_db_name);
207 block_.open(block_db_name);
208 forward_.open(forward_db_name);
210 if (sock_.has_peername() && !IP::is_private(sock_.us_c_str())) {
211 auto fcrdns = DNS::fcrdns(res_, sock_.us_c_str());
212 for (auto const& fcr : fcrdns) {
213 server_fcrdns_.emplace_back(fcr);
217 server_identity_ = [this] {
218 auto const id_from_env{getenv("GHSMTP_SERVER_ID")};
219 if (id_from_env)
220 return std::string{id_from_env};
222 auto const hostname{osutil::get_hostname()};
223 if (hostname.find('.') != std::string::npos)
224 return hostname;
226 if (!server_fcrdns_.empty()) {
227 // first result should be shortest
228 return server_fcrdns_.front().ascii();
231 auto const us_c_str = sock_.us_c_str();
232 if (us_c_str && !IP::is_private(us_c_str)) {
233 return IP::to_address_literal(us_c_str);
236 LOG(FATAL) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
237 return ""s;
238 }();
240 // send_.set_sender(server_identity_);
242 max_msg_size(Config::max_msg_size_initial);
245 void Session::max_msg_size(size_t max)
247 max_msg_size_ = max; // number to advertise via RFC 1870
249 if (FLAGS_max_read) {
250 sock_.set_max_read(FLAGS_max_read);
252 else {
253 auto const overhead = std::max(max / 10, size_t(2048));
254 sock_.set_max_read(max + overhead);
258 void Session::bad_host_(char const* msg) const
260 if (sock_.has_peername()) {
261 // On my systems, this pattern triggers a fail2ban rule that
262 // blocks connections from this IP address on port 25 for a few
263 // days. See <https://www.fail2ban.org/> for more info.
264 syslog(LOG_MAIL | LOG_WARNING, "bad host [%s] %s", sock_.them_c_str(), msg);
266 std::exit(EXIT_SUCCESS);
269 void Session::reset_()
271 // RSET does not force another EHLO/HELO, the one piece of per
272 // transaction data saved is client_identity_:
274 // client_identity_.clear(); <-- not cleared!
276 reverse_path_.clear();
277 forward_path_.clear();
278 spf_received_.clear();
279 // fwd_path_.clear();
280 // fwd_from_.clear();
281 // rep_info_.clear();
283 binarymime_ = false;
284 smtputf8_ = false;
285 // prdr_ = false;
287 if (msg_) {
288 msg_.reset();
291 max_msg_size(max_msg_size());
293 state_ = xact_step::mail;
294 // send_.rset();
297 // Return codes from connection establishment are 220 or 554, according
298 // to RFC 5321. That's it.
300 void Session::greeting()
302 CHECK(state_ == xact_step::helo);
304 if (sock_.has_peername()) {
305 close(2); // if we're a networked program, never send to stderr
307 std::string error_msg;
308 if (!verify_ip_address_(error_msg)) {
309 LOG(INFO) << "IP address blocked: " << error_msg;
310 bad_host_(error_msg.c_str());
313 /******************************************************************
314 <https://tools.ietf.org/html/rfc5321#section-4.3.1> says:
316 4.3. Sequencing of Commands and Replies
318 4.3.1. Sequencing Overview
320 The communication between the sender and receiver is an alternating
321 dialogue, controlled by the sender. As such, the sender issues a
322 command and the receiver responds with a reply. Unless other
323 arrangements are negotiated through service extensions, the sender
324 MUST wait for this response before sending further commands. One
325 important reply is the connection greeting. Normally, a receiver
326 will send a 220 "Service ready" reply when the connection is
327 completed. The sender SHOULD wait for this greeting message before
328 sending any commands.
330 So which is it?
332 “…the receiver responds with a reply.”
333 “…the sender MUST wait for this response…”
334 “One important reply is the connection greeting.”
335 “The sender SHOULD wait for this greeting…”
337 So is it MUST or SHOULD? I enforce MUST.
338 *******************************************************************/
340 // Wait a bit of time for pre-greeting traffic.
341 if (!(ip_allowed_ || fcrdns_allowed_)) {
342 if (sock_.input_ready(Config::greeting_wait)) {
343 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
344 LOG(INFO) << "input before any greeting from " << client_;
345 bad_host_("input before any greeting");
347 // Give a half greeting and wait again.
348 out_() << "220-" << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
349 if (sock_.input_ready(Config::greeting_wait)) {
350 out_() << "421 4.3.2 not accepting network messages\r\n" << std::flush;
351 LOG(INFO) << "input before full greeting from " << client_;
352 bad_host_("input before full greeting");
355 LOG(INFO) << "connect from " << client_;
358 out_() << "220 " << server_id_() << " ESMTP - ghsmtp\r\n" << std::flush;
360 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr)) {
361 alarm(2 * 60); // initial alarm
365 void Session::flush() { out_() << std::flush; }
367 void Session::last_in_group_(std::string_view verb)
369 if (sock_.input_ready(std::chrono::seconds(0))) {
370 LOG(WARNING) << "pipelining error; input ready processing " << verb;
374 void Session::check_for_pipeline_error_(std::string_view verb)
376 if (!(FLAGS_use_pipelining && extensions_)) {
377 if (sock_.input_ready(std::chrono::seconds(0))) {
378 LOG(WARNING) << "pipelining error; input ready processing " << verb;
383 void Session::lo_(char const* verb, std::string_view client_identity)
385 last_in_group_(verb);
386 reset_();
388 if (client_identity_ != client_identity) {
389 client_identity_ = client_identity;
391 std::string error_msg;
392 if (!verify_client_(client_identity_, error_msg)) {
393 LOG(INFO) << "client identity blocked: " << error_msg;
394 bad_host_(error_msg.c_str());
398 if (*verb == 'H') {
399 extensions_ = false;
400 out_() << "250 " << server_id_() << "\r\n";
403 if (*verb == 'E') {
404 extensions_ = true;
406 if (sock_.has_peername()) {
407 out_() << "250-" << server_id_() << " at your service, " << client_
408 << "\r\n";
410 else {
411 out_() << "250-" << server_id_() << "\r\n";
414 out_() << "250-SIZE " << max_msg_size() << "\r\n"; // RFC 1870
415 out_() << "250-8BITMIME\r\n"; // RFC 6152
417 if (FLAGS_rrvs) {
418 out_() << "250-RRVS\r\n"; // RFC 7293
421 // out_() << "250-PRDR\r\n"; // draft-hall-prdr-00.txt
423 if (sock_.tls()) {
424 // Check sasl sources for auth types.
425 // out_() << "250-AUTH PLAIN\r\n";
426 out_() << "250-REQUIRETLS\r\n"; // RFC 8689
428 else {
429 // If we're not already TLS, offer TLS
430 out_() << "250-STARTTLS\r\n"; // RFC 3207
433 out_() << "250-ENHANCEDSTATUSCODES\r\n"; // RFC 2034
435 if (FLAGS_use_pipelining) {
436 out_() << "250-PIPELINING\r\n"; // RFC 2920
439 out_() << "250-BINARYMIME\r\n"; // RFC 3030
440 out_() << "250-CHUNKING\r\n"; // RFC 3030
441 out_() << "250 SMTPUTF8\r\n"; // RFC 6531
444 out_() << std::flush;
446 if (sock_.has_peername()) {
447 if (std::find(begin(client_fcrdns_), end(client_fcrdns_),
448 client_identity_) != end(client_fcrdns_)) {
449 LOG(INFO) << verb << " " << client_identity << " from "
450 << sock_.them_address_literal();
452 else {
453 LOG(INFO) << verb << " " << client_identity << " from " << client_;
456 else {
457 LOG(INFO) << verb << " " << client_identity;
461 void Session::mail_from(Mailbox&& reverse_path, parameters_t const& parameters)
463 check_for_pipeline_error_("MAIL FROM");
465 switch (state_) {
466 case xact_step::helo:
467 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
468 LOG(WARNING) << "'MAIL FROM' before HELO/EHLO"
469 << (sock_.has_peername() ? " from " : "") << client_;
470 return;
471 case xact_step::mail: break;
472 case xact_step::rcpt:
473 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
474 LOG(WARNING) << "nested MAIL command"
475 << (sock_.has_peername() ? " from " : "") << client_;
476 return;
477 case xact_step::data:
478 case xact_step::bdat:
479 out_() << "503 5.5.1 sequence error, expecting DATA/BDAT\r\n" << std::flush;
480 LOG(WARNING) << "nested MAIL command"
481 << (sock_.has_peername() ? " from " : "") << client_;
482 return;
483 case xact_step::rset:
484 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
485 LOG(WARNING) << "error state must be cleared with a RSET"
486 << (sock_.has_peername() ? " from " : "") << client_;
487 return;
490 if (!verify_from_params_(parameters)) {
491 return;
494 if (!smtputf8_ && !is_ascii(reverse_path.local_part())) {
495 LOG(WARNING) << "non ascii reverse_path \"" << reverse_path
496 << "\" without SMTPUTF8 paramater";
499 std::string error_msg;
500 if (!verify_sender_(reverse_path, error_msg)) {
501 LOG(INFO) << "verify sender failed: " << error_msg;
502 bad_host_(error_msg.c_str());
505 reverse_path_ = std::move(reverse_path);
506 // fwd_path_.clear();
507 // fwd_from_.clear();
508 forward_path_.clear();
509 out_() << "250 2.1.0 MAIL FROM OK\r\n";
510 // No flush RFC-2920 section 3.1, this could be part of a command group.
512 fmt::memory_buffer params;
513 for (auto const& [name, value] : parameters) {
514 fmt::format_to(params, " {}", name);
515 if (!value.empty()) {
516 fmt::format_to(params, "={}", value);
519 LOG(INFO) << "MAIL FROM:<" << reverse_path_ << ">" << fmt::to_string(params);
521 state_ = xact_step::rcpt;
524 // bool Session::forward_to_(std::string const& forward, Mailbox const& rcpt_to)
525 // {
526 // // If we're already forwarding or replying, reject
527 // if (!fwd_path_.empty() || !rep_info_.empty()) {
528 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
529 // stopped\r\n"
530 // << std::flush;
531 // LOG(WARNING) << "failed to forward to <" << forward
532 // << "> already forwarding or replying for: " << rcpt_to;
533 // return false;
534 // }
536 // fwd_path_ = Mailbox(forward);
537 // fwd_from_ = rcpt_to;
539 // // New bounce address
540 // Reply::from_to bounce;
541 // bounce.mail_from = reverse_path_.as_string();
543 // auto const new_bounce = srs_.enc_bounce(bounce, server_id_().c_str());
545 // auto const mail_from = Mailbox(new_bounce);
547 // std::string error_msg;
548 // if (!send_.mail_from_rcpt_to(res_, mail_from, fwd_path_, error_msg)) {
549 // out_() << error_msg << std::flush;
550 // LOG(WARNING) << "failed to forward <" << fwd_path_ << "> " << error_msg;
551 // return false;
552 // }
554 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> forwarding to == <" << fwd_path_
555 // << ">";
556 // return true;
557 // }
559 // bool Session::reply_to_(Reply::from_to const& reply_info, Mailbox const&
560 // rcpt_to)
561 // {
562 // // If we're already forwarding or replying, reject
563 // if (!fwd_path_.empty() || !rep_info_.empty()) {
564 // out_() << "432 4.3.0 Recipient's incoming mail queue has been
565 // stopped\r\n"
566 // << std::flush;
567 // LOG(WARNING) << "failed to reply to <" << reply_info.mail_from
568 // << "> already forwarding or replying for: " << rcpt_to;
569 // return false;
570 // }
572 // rep_info_ = reply_info;
574 // Mailbox const from(rep_info_.rcpt_to_local_part, server_identity_);
575 // Mailbox const to(rep_info_.mail_from);
577 // std::string error_msg;
578 // if (!send_.mail_from_rcpt_to(res_, from, to, error_msg)) {
579 // out_() << error_msg << std::flush;
580 // LOG(WARNING) << "failed to reply from <" << from << "> to <" << to << ">
581 // "
582 // << error_msg;
583 // return false;
584 // }
586 // LOG(INFO) << "RCPT TO:<" << rcpt_to << "> is a reply to "
587 // << rep_info_.mail_from << " from " <<
588 // rep_info_.rcpt_to_local_part;
589 // return true;
590 // }
592 void Session::rcpt_to(Mailbox&& forward_path, parameters_t const& parameters)
594 check_for_pipeline_error_("RCPT TO");
596 switch (state_) {
597 case xact_step::helo:
598 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
599 LOG(WARNING) << "'RCPT TO' before HELO/EHLO"
600 << (sock_.has_peername() ? " from " : "") << client_;
601 return;
602 case xact_step::mail:
603 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
604 LOG(WARNING) << "'RCPT TO' before 'MAIL FROM'"
605 << (sock_.has_peername() ? " from " : "") << client_;
606 return;
607 case xact_step::rcpt:
608 case xact_step::data: break;
609 case xact_step::bdat:
610 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
611 LOG(WARNING) << "'RCPT TO' during BDAT transfer"
612 << (sock_.has_peername() ? " from " : "") << client_;
613 return;
614 case xact_step::rset:
615 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
616 LOG(WARNING) << "error state must be cleared with a RSET"
617 << (sock_.has_peername() ? " from " : "") << client_;
618 return;
621 if (!verify_rcpt_params_(parameters))
622 return;
624 if (!verify_recipient_(forward_path))
625 return;
627 if (!smtputf8_ && !is_ascii(forward_path.local_part())) {
628 LOG(WARNING) << "non ascii forward_path \"" << forward_path
629 << "\" without SMTPUTF8 paramater";
632 if (forward_path_.size() >= Config::max_recipients_per_message) {
633 out_() << "452 4.5.3 too many recipients\r\n" << std::flush;
634 LOG(WARNING) << "too many recipients <" << forward_path << ">";
635 return;
637 // no check for dups, postfix doesn't
638 forward_path_.emplace_back(std::move(forward_path));
640 Mailbox const& rcpt_to_mbx = forward_path_.back();
642 LOG(INFO) << "RCPT TO:<" << rcpt_to_mbx << ">";
644 // auto const rcpt_to_str = rcpt_to_mbx.as_string();
646 // if (auto reply = srs_.dec_reply(rcpt_to_mbx.local_part()); reply) {
647 // if (!reply_to_(*reply, rcpt_to_mbx))
648 // return;
649 // }
650 // else if (auto const forward = forward_.find(rcpt_to_str.c_str()); forward)
651 // {
652 // if (!forward_to_(*forward, rcpt_to_mbx))
653 // return;
654 // }
655 // else {
656 // LOG(INFO) << "RCPT TO:<" << rcpt_to_str << ">";
657 // }
659 // No flush RFC-2920 section 3.1, this could be part of a command group.
660 out_() << "250 2.1.5 RCPT TO OK\r\n";
662 state_ = xact_step::data;
665 // The headers Return-Path:, Received-SPF:, and Received: are returned
666 // as a string.
668 std::string Session::added_headers_(MessageStore const& msg)
670 auto const protocol{[this]() {
671 if (sock_.tls() && !extensions_) {
672 LOG(WARNING) << "TLS active without extensions";
674 // <https://www.iana.org/assignments/mail-parameters/mail-parameters.xhtml#mail-parameters-5>
675 if (smtputf8_)
676 return sock_.tls() ? "UTF8SMTPS" : "UTF8SMTP";
677 else if (sock_.tls())
678 return "ESMTPS";
679 else if (extensions_)
680 return "ESMTP";
681 else
682 return "SMTP";
683 }()};
685 fmt::memory_buffer headers;
687 // Return-Path:
688 fmt::format_to(headers, "Return-Path: <{}>\r\n", reverse_path_);
690 // Received-SPF:
691 if (!spf_received_.empty()) {
692 fmt::format_to(headers, "{}\r\n", spf_received_);
695 // Received:
696 // <https://tools.ietf.org/html/rfc5321#section-4.4>
697 fmt::format_to(headers, "Received: from {}", client_identity_.utf8());
698 if (sock_.has_peername()) {
699 fmt::format_to(headers, " ({})", client_);
701 fmt::format_to(headers, "\r\n\tby {} with {} id {}", server_identity_.utf8(),
702 protocol, msg.id());
703 if (forward_path_.size()) {
704 fmt::format_to(headers, "\r\n\tfor <{}>", forward_path_[0]);
705 for (auto i = 1u; i < forward_path_.size(); ++i)
706 fmt::format_to(headers, ",\r\n\t <{}>", forward_path_[i]);
708 std::string const tls_info{sock_.tls_info()};
709 if (tls_info.length()) {
710 fmt::format_to(headers, "\r\n\t({})", tls_info);
712 fmt::format_to(headers, ";\r\n\t{}\r\n", msg.when());
714 return fmt::to_string(headers);
717 namespace {
718 bool lookup_domain(CDB& cdb, Domain const& domain)
720 if (!domain.empty()) {
721 if (cdb.contains(domain.ascii())) {
722 return true;
724 if (domain.is_unicode() && cdb.contains(domain.utf8())) {
725 return true;
728 return false;
730 } // namespace
732 std::tuple<Session::SpamStatus, std::string> Session::spam_status_()
734 if (spf_result_ == SPF::Result::FAIL && !ip_allowed_)
735 return {SpamStatus::spam, "SPF failed"};
737 // These should have already been rejected by verify_client_().
738 if ((reverse_path_.domain() == "localhost.local") ||
739 (reverse_path_.domain() == "localhost"))
740 return {SpamStatus::spam, "bogus reverse_path"};
742 std::vector<std::string> why_ham;
744 // Anything enciphered tastes a lot like ham.
745 if (sock_.tls())
746 why_ham.emplace_back("they used TLS");
748 if (spf_result_ == SPF::Result::PASS) {
749 if (lookup_domain(allow_, spf_sender_domain_)) {
750 why_ham.emplace_back(fmt::format("SPF sender domain ({}) is allowed",
751 spf_sender_domain_.utf8()));
753 else {
754 auto tld_dom{tld_db_.get_registered_domain(spf_sender_domain_.ascii())};
755 if (tld_dom && allow_.contains(tld_dom)) {
756 why_ham.emplace_back(fmt::format(
757 "SPF sender registered domain ({}) is allowed", tld_dom));
762 if (fcrdns_allowed_)
763 why_ham.emplace_back(
764 fmt::format("FCrDNS (or it's registered domain) is allowed"));
766 if (!why_ham.empty())
767 return {SpamStatus::ham,
768 fmt::format("{}", fmt::join(std::begin(why_ham), std::end(why_ham),
769 ", and "))};
771 return {SpamStatus::spam, "it's not ham"};
774 static std::string folder(Session::SpamStatus status,
775 std::vector<Mailbox> const& forward_path,
776 Mailbox const& reverse_path)
778 if (reverse_path ==
779 Mailbox("gene.hightower+caf_=forwarded-gmail=digilicious.com@gmail.com"))
780 return ".Gmail";
782 if (status == Session::SpamStatus::spam)
783 return ".Junk";
785 if (iends_with(forward_path[0].local_part(), "-at-duck"))
786 return ".JunkDuck";
788 return "";
791 bool Session::msg_new()
793 CHECK((state_ == xact_step::data) || (state_ == xact_step::bdat));
795 auto const& [status, reason]{spam_status_()};
797 LOG(INFO) << ((status == SpamStatus::ham) ? "ham since " : "spam since ")
798 << reason;
800 // All sources of ham get a fresh 5 minute timeout per message.
801 if (status == SpamStatus::ham) {
802 if ((!FLAGS_immortal) && (getenv("GHSMTP_IMMORTAL") == nullptr))
803 alarm(5 * 60);
806 msg_ = std::make_unique<MessageStore>();
808 if (!FLAGS_max_write)
809 FLAGS_max_write = max_msg_size();
811 try {
812 msg_->open(server_id_(), FLAGS_max_write,
813 folder(status, forward_path_, reverse_path_));
814 auto const hdrs{added_headers_(*(msg_.get()))};
815 msg_->write(hdrs);
817 // fmt::memory_buffer spam_status;
818 // fmt::format_to(spam_status, "X-Spam-Status: {}, {}\r\n",
819 // ((status == SpamStatus::spam) ? "Yes" : "No"), reason);
820 // msg_->write(spam_status.data(), spam_status.size());
822 LOG(INFO) << "Spam-Status: "
823 << ((status == SpamStatus::spam) ? "Yes" : "No") << ", "
824 << reason;
826 return true;
828 catch (std::system_error const& e) {
829 switch (errno) {
830 case ENOSPC:
831 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
832 LOG(ERROR) << "no space";
833 msg_->trash();
834 msg_.reset();
835 return false;
837 default:
838 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
839 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
840 LOG(ERROR) << e.what();
841 msg_->trash();
842 msg_.reset();
843 return false;
846 catch (std::exception const& e) {
847 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
848 LOG(ERROR) << e.what();
849 msg_->trash();
850 msg_.reset();
851 return false;
854 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
855 LOG(ERROR) << "msg_new failed with no exception caught";
856 msg_->trash();
857 msg_.reset();
858 return false;
861 bool Session::msg_write(char const* s, std::streamsize count)
863 if ((state_ != xact_step::data) && (state_ != xact_step::bdat))
864 return false;
866 if (!msg_)
867 return false;
869 try {
870 if (msg_->write(s, count))
871 return true;
873 catch (std::system_error const& e) {
874 switch (errno) {
875 case ENOSPC:
876 out_() << "452 4.3.1 insufficient system storage\r\n" << std::flush;
877 LOG(ERROR) << "no space";
878 msg_->trash();
879 msg_.reset();
880 return false;
882 default:
883 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
884 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
885 LOG(ERROR) << e.what();
886 msg_->trash();
887 msg_.reset();
888 return false;
891 catch (std::exception const& e) {
892 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
893 LOG(ERROR) << e.what();
894 msg_->trash();
895 msg_.reset();
896 return false;
899 out_() << "451 4.0.0 mail system error\r\n" << std::flush;
900 LOG(ERROR) << "msg_write failed with no exception caught";
901 msg_->trash();
902 msg_.reset();
903 return false;
906 bool Session::data_start()
908 last_in_group_("DATA");
910 switch (state_) {
911 case xact_step::helo:
912 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
913 LOG(WARNING) << "'DATA' before HELO/EHLO"
914 << (sock_.has_peername() ? " from " : "") << client_;
915 return false;
916 case xact_step::mail:
917 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
918 LOG(WARNING) << "'DATA' before 'MAIL FROM'"
919 << (sock_.has_peername() ? " from " : "") << client_;
920 return false;
921 case xact_step::rcpt:
923 /******************************************************************
924 <https://tools.ietf.org/html/rfc5321#section-3.3> says:
926 The DATA command can fail at only two points in the protocol exchange:
928 If there was no MAIL, or no RCPT, command, or all such commands were
929 rejected, the server MAY return a "command out of sequence" (503) or
930 "no valid recipients" (554) reply in response to the DATA command.
932 However, <https://tools.ietf.org/html/rfc2033#section-4.2> says:
934 The additional restriction is that when there have been no successful
935 RCPT commands in the mail transaction, the DATA command MUST fail
936 with a 503 reply code.
938 Therefore I will send the reply code that is valid for both, and
939 do the same for the BDAT case.
940 *******************************************************************/
942 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
943 LOG(WARNING) << "no valid recipients"
944 << (sock_.has_peername() ? " from " : "") << client_;
945 return false;
946 case xact_step::data: break;
947 case xact_step::bdat:
948 out_() << "503 5.5.1 sequence error, expecting BDAT\r\n" << std::flush;
949 LOG(WARNING) << "'DATA' during BDAT transfer"
950 << (sock_.has_peername() ? " from " : "") << client_;
951 return false;
952 case xact_step::rset:
953 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
954 LOG(WARNING) << "error state must be cleared with a RSET"
955 << (sock_.has_peername() ? " from " : "") << client_;
956 return false;
959 if (binarymime_) {
960 out_() << "503 5.5.1 sequence error, DATA does not support BINARYMIME\r\n"
961 << std::flush;
962 LOG(WARNING) << "DATA does not support BINARYMIME";
963 state_ = xact_step::rset; // RFC 3030 section 3 page 5
964 return false;
967 if (!msg_new()) {
968 LOG(ERROR) << "msg_new() failed";
969 return false;
972 out_() << "354 go, end with <CR><LF>.<CR><LF>\r\n" << std::flush;
973 LOG(INFO) << "DATA";
974 return true;
977 // bool Session::do_forward_(message::parsed& msg)
978 // {
979 // auto msg_fwd = msg;
981 // // Generate a reply address
982 // Reply::from_to reply;
983 // reply.mail_from = msg_fwd.dmarc_from;
984 // reply.rcpt_to_local_part = fwd_from_.local_part();
986 // auto const reply_addr =
987 // fmt::format("{}@{}", srs_.enc_reply(reply), server_id_());
989 // auto const munging = false;
991 // auto const sender = server_identity_.ascii().c_str();
992 // auto const selector = FLAGS_selector.c_str();
993 // auto const key_file =
994 // (config_path_ / FLAGS_selector).replace_extension("private");
995 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
997 // if (munging) {
998 // auto const from_hdr =
999 // fmt::format("From: \"{} via\" <@>", msg_fwd.dmarc_from, reply_addr);
1000 // message::rewrite_from_to(msg_fwd, from_hdr, "", sender, selector,
1001 // key_file);
1002 // }
1003 // else {
1004 // auto const reply_to_hdr = fmt::format("Reply-To: {}", reply_addr);
1005 // message::rewrite_from_to(msg_fwd, "", reply_to_hdr, sender, selector,
1006 // key_file);
1007 // }
1009 // // Forward it on
1010 // if (!send_.send(msg_fwd.as_string())) {
1011 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1012 // "stopped\r\n"
1013 // << std::flush;
1015 // LOG(ERROR) << "failed to send for " << fwd_path_;
1016 // return false;
1017 // }
1019 // LOG(INFO) << "successfully sent for " << fwd_path_;
1020 // return true;
1021 // }
1023 // bool Session::do_reply_(message::parsed& msg)
1024 // {
1025 // Mailbox to_mbx(rep_info_.mail_from);
1026 // Mailbox from_mbx(rep_info_.rcpt_to_local_part, server_identity_);
1028 // auto reply = std::make_unique<MessageStore>();
1029 // reply->open(server_id_(), FLAGS_max_write, ".Drafts");
1031 // auto const date{Now{}};
1032 // auto const pill{Pill{}};
1033 // auto const mid_str =
1034 // fmt::format("<{}.{}@{}>", date.sec(), pill, server_identity_);
1036 // fmt::memory_buffer bfr;
1038 // fmt::format_to(bfr, "From: <{}>\r\n", from_mbx);
1039 // fmt::format_to(bfr, "To: <{}>\r\n", to_mbx);
1041 // fmt::format_to(bfr, "Date: {}\r\n", date.c_str());
1043 // fmt::format_to(bfr, "Message-ID: {}\r\n", mid_str.c_str());
1045 // if (!msg.get_header(message::Subject).empty()) {
1046 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
1047 // msg.get_header(message::Subject));
1048 // }
1049 // else {
1050 // fmt::format_to(bfr, "{}: {}\r\n", message::Subject,
1051 // "Reply to your message");
1052 // }
1054 // if (!msg.get_header(message::In_Reply_To).empty()) {
1055 // fmt::format_to(bfr, "{}: {}\r\n", message::In_Reply_To,
1056 // msg.get_header(message::In_Reply_To));
1057 // }
1059 // if (!msg.get_header(message::MIME_Version).empty() &&
1060 // msg.get_header(message::Content_Type).empty()) {
1061 // fmt::format_to(bfr, "{}: {}\r\n", message::MIME_Version,
1062 // msg.get_header(message::MIME_Version));
1063 // fmt::format_to(bfr, "{}: {}\r\n", message::Content_Type,
1064 // msg.get_header(message::Content_Type));
1065 // }
1067 // reply->write(fmt::to_string(bfr));
1069 // if (!msg.body.empty()) {
1070 // reply->write("\r\n");
1071 // reply->write(msg.body);
1072 // }
1074 // auto const msg_data = reply->freeze();
1075 // message::parsed msg_reply;
1076 // CHECK(msg_reply.parse(msg_data));
1078 // auto const sender = server_identity_.ascii().c_str();
1079 // auto const selector = FLAGS_selector.c_str();
1080 // auto const key_file =
1081 // (config_path_ / FLAGS_selector).replace_extension("private");
1082 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1084 // message::dkim_sign(msg_reply, sender, selector, key_file);
1086 // if (!send_.send(msg_reply.as_string())) {
1087 // out_() << "432 4.3.0 Recipient's incoming mail queue has been "
1088 // "stopped\r\n"
1089 // << std::flush;
1091 // LOG(ERROR) << "send failed for reply to " << to_mbx << " from " <<
1092 // from_mbx; return false;
1093 // }
1095 // LOG(INFO) << "successful reply to " << to_mbx << " from " << from_mbx;
1096 // return true;
1097 // }
1099 bool Session::do_deliver_()
1101 CHECK(msg_);
1103 // auto const sender = server_identity_.ascii().c_str();
1104 // auto const selector = FLAGS_selector.c_str();
1105 // auto const key_file =
1106 // (config_path_ / FLAGS_selector).replace_extension("private");
1107 // CHECK(fs::exists(key_file)) << "can't find key file " << key_file;
1109 try {
1110 // auto const msg_data = msg_->freeze();
1112 // message::parsed msg;
1114 // // Only deal in RFC-5322 Mail Objects.
1115 // bool const message_parsed = msg.parse(msg_data);
1116 // if (message_parsed) {
1118 // // remove any Return-Path
1119 // message::remove_delivery_headers(msg);
1121 // auto const authentic =
1122 // message_parsed &&
1123 // message::authentication(msg, sender, selector, key_file);
1125 // // write a new Return-Path
1126 // msg_->write(fmt::format("Return-Path: <{}>\r\n", reverse_path_));
1128 // for (auto const h : msg.headers) {
1129 // msg_->write(h.as_string());
1130 // msg_->write("\r\n");
1131 // }
1132 // if (!msg.body.empty()) {
1133 // msg_->write("\r\n");
1134 // msg_->write(msg.body);
1135 // }
1137 msg_->deliver();
1139 // if (authentic && !fwd_path_.empty()) {
1140 // if (!do_forward_(msg))
1141 // return false;
1142 // }
1143 // if (authentic && !rep_info_.empty()) {
1144 // if (!do_reply_(msg))
1145 // return false;
1146 // }
1147 // }
1149 msg_->close();
1151 catch (std::system_error const& e) {
1152 switch (errno) {
1153 case ENOSPC:
1154 out_() << "452 4.3.1 mail system full\r\n" << std::flush;
1155 LOG(ERROR) << "no space";
1156 msg_->trash();
1157 reset_();
1158 return false;
1160 default:
1161 out_() << "550 5.0.0 mail system error\r\n" << std::flush;
1162 if (errno)
1163 LOG(ERROR) << "errno==" << errno << ": " << strerror(errno);
1164 LOG(ERROR) << e.what();
1165 msg_->trash();
1166 reset_();
1167 return false;
1171 return true;
1174 void Session::data_done()
1176 CHECK((state_ == xact_step::data));
1178 if (msg_ && msg_->size_error()) {
1179 data_size_error();
1180 return;
1183 // if (prdr_) {
1184 // out_() << "353\r\n";
1185 // for (auto fp : forward_path_) {
1186 // out_() << "250 2.1.5 RCPT TO OK\r\n";
1187 // }
1188 // }
1190 // Check for and act on magic "wait" address.
1192 using namespace boost::xpressive;
1194 sregex const rex = icase("wait-data-") >> (secs_ = +_d);
1195 smatch what;
1197 for (auto fp : forward_path_) {
1198 if (regex_match(fp.local_part(), what, rex) ||
1199 regex_match(fp.local_part(), what, all_rex)) {
1200 auto const str = what[secs_].str();
1201 LOG(INFO) << "waiting at DATA " << str << " seconds";
1202 long value = 0;
1203 std::from_chars(str.data(), str.data() + str.size(), value);
1204 google::FlushLogFiles(google::INFO);
1205 out_() << std::flush;
1206 sleep(value);
1207 LOG(INFO) << "done waiting";
1212 do_deliver_();
1214 out_() << "250 2.0.0 DATA OK\r\n" << std::flush;
1215 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1216 << msg_->id();
1217 reset_();
1220 void Session::data_size_error()
1222 out_().clear(); // clear possible eof from input side
1223 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1224 if (msg_) {
1225 msg_->trash();
1227 LOG(WARNING) << "DATA size error";
1228 reset_();
1231 void Session::data_error()
1233 out_().clear(); // clear possible eof from input side
1234 out_() << "554 5.3.0 message error of some kind\r\n" << std::flush;
1235 if (msg_) {
1236 msg_->trash();
1238 LOG(WARNING) << "DATA error";
1239 reset_();
1242 bool Session::bdat_start(size_t n)
1244 // In practice, this one gets pipelined.
1245 // last_in_group_("BDAT");
1247 switch (state_) {
1248 case xact_step::helo:
1249 out_() << "503 5.5.1 sequence error, expecting HELO/EHLO\r\n" << std::flush;
1250 LOG(WARNING) << "'BDAT' before HELO/EHLO"
1251 << (sock_.has_peername() ? " from " : "") << client_;
1252 return false;
1253 case xact_step::mail:
1254 out_() << "503 5.5.1 sequence error, expecting MAIL\r\n" << std::flush;
1255 LOG(WARNING) << "'BDAT' before 'MAIL FROM'"
1256 << (sock_.has_peername() ? " from " : "") << client_;
1257 return false;
1258 case xact_step::rcpt:
1259 // See comment in data_start()
1260 out_() << "503 5.5.1 sequence error, expecting RCPT\r\n" << std::flush;
1261 LOG(WARNING) << "no valid recipients"
1262 << (sock_.has_peername() ? " from " : "") << client_;
1263 return false;
1264 case xact_step::data: // first bdat
1265 break;
1266 case xact_step::bdat: return true;
1267 case xact_step::rset:
1268 out_() << "503 5.5.1 sequence error, expecting RSET\r\n" << std::flush;
1269 LOG(WARNING) << "error state must be cleared with a RSET"
1270 << (sock_.has_peername() ? " from " : "") << client_;
1271 return false;
1274 state_ = xact_step::bdat;
1276 return msg_new();
1279 void Session::bdat_done(size_t n, bool last)
1281 if (state_ != xact_step::bdat) {
1282 bdat_seq_error();
1283 return;
1286 if (!msg_) {
1287 return;
1290 if (msg_->size_error()) {
1291 bdat_size_error();
1292 return;
1295 if (!last) {
1296 out_() << "250 2.0.0 BDAT " << n << " OK\r\n" << std::flush;
1297 LOG(INFO) << "BDAT " << n;
1298 return;
1301 // Check for and act on magic "wait" address.
1303 using namespace boost::xpressive;
1305 sregex const rex = icase("wait-bdat-") >> (secs_ = +_d);
1306 smatch what;
1308 for (auto fp : forward_path_) {
1309 if (regex_match(fp.local_part(), what, rex) ||
1310 regex_match(fp.local_part(), what, all_rex)) {
1311 auto const str = what[secs_].str();
1312 LOG(INFO) << "waiting at BDAT " << str << " seconds";
1313 long value = 0;
1314 std::from_chars(str.data(), str.data() + str.size(), value);
1315 google::FlushLogFiles(google::INFO);
1316 out_() << std::flush;
1317 sleep(value);
1318 LOG(INFO) << "done waiting";
1323 do_deliver_();
1325 out_() << "250 2.0.0 BDAT " << n << " LAST OK\r\n" << std::flush;
1326 LOG(INFO) << "BDAT " << n << " LAST";
1327 LOG(INFO) << "message delivered, " << msg_->size() << " octets, with id "
1328 << msg_->id();
1329 reset_();
1332 void Session::bdat_size_error()
1334 out_().clear(); // clear possible eof from input side
1335 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1336 if (msg_) {
1337 msg_->trash();
1339 LOG(WARNING) << "BDAT size error";
1340 reset_();
1343 void Session::bdat_seq_error()
1345 out_().clear(); // clear possible eof from input side
1346 out_() << "503 5.5.1 BDAT sequence error\r\n" << std::flush;
1347 if (msg_) {
1348 msg_->trash();
1350 LOG(WARNING) << "BDAT sequence error";
1351 reset_();
1354 void Session::bdat_io_error()
1356 out_().clear(); // clear possible eof from input side
1357 out_() << "503 5.5.1 BDAT I/O error\r\n" << std::flush;
1358 if (msg_) {
1359 msg_->trash();
1361 LOG(WARNING) << "BDAT I/O error";
1362 reset_();
1365 void Session::rset()
1367 out_() << "250 2.1.5 RSET OK\r\n";
1368 // No flush RFC-2920 section 3.1, this could be part of a command group.
1369 LOG(INFO) << "RSET";
1370 reset_();
1373 void Session::noop(std::string_view str)
1375 last_in_group_("NOOP");
1376 out_() << "250 2.0.0 NOOP OK\r\n" << std::flush;
1377 LOG(INFO) << "NOOP" << (str.length() ? " " : "") << str;
1380 void Session::vrfy(std::string_view str)
1382 last_in_group_("VRFY");
1383 out_() << "252 2.1.5 try it\r\n" << std::flush;
1384 LOG(INFO) << "VRFY" << (str.length() ? " " : "") << str;
1387 void Session::help(std::string_view str)
1389 out_() << "214 2.0.0 see https://digilicious.com/smtp.html\r\n" << std::flush;
1390 LOG(INFO) << "HELP" << (str.length() ? " " : "") << str;
1393 void Session::quit()
1395 // send_.quit();
1396 // last_in_group_("QUIT");
1397 out_() << "221 2.0.0 closing connection\r\n" << std::flush;
1398 LOG(INFO) << "QUIT";
1399 exit_();
1402 void Session::auth()
1404 out_() << "454 4.7.0 authentication failure\r\n" << std::flush;
1405 LOG(INFO) << "AUTH";
1406 bad_host_("auth");
1409 void Session::error(std::string_view log_msg)
1411 out_() << "421 4.3.5 system error: " << log_msg << "\r\n" << std::flush;
1412 LOG(WARNING) << log_msg;
1415 void Session::cmd_unrecognized(std::string_view cmd)
1417 auto const escaped{esc(cmd)};
1418 LOG(WARNING) << "command unrecognized: \"" << escaped << "\"";
1420 if (++n_unrecognized_cmds_ >= Config::max_unrecognized_cmds) {
1421 out_() << "500 5.5.1 command unrecognized: \"" << escaped
1422 << "\" exceeds limit\r\n"
1423 << std::flush;
1424 LOG(WARNING) << n_unrecognized_cmds_
1425 << " unrecognized commands is too many";
1426 exit_();
1429 out_() << "500 5.5.1 command unrecognized: \"" << escaped << "\"\r\n"
1430 << std::flush;
1433 void Session::bare_lf()
1435 // Error code used by Office 365.
1436 out_() << "554 5.6.11 bare LF\r\n" << std::flush;
1437 LOG(WARNING) << "bare LF";
1438 exit_();
1441 void Session::max_out()
1443 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1444 LOG(WARNING) << "message size maxed out";
1445 exit_();
1448 void Session::time_out()
1450 out_() << "421 4.4.2 time-out\r\n" << std::flush;
1451 LOG(WARNING) << "time-out" << (sock_.has_peername() ? " from " : "")
1452 << client_;
1453 exit_();
1456 void Session::starttls()
1458 last_in_group_("STARTTLS");
1459 if (sock_.tls()) {
1460 out_() << "554 5.5.1 TLS already active\r\n" << std::flush;
1461 LOG(WARNING) << "STARTTLS issued with TLS already active";
1463 else if (!extensions_) {
1464 out_() << "554 5.5.1 TLS not avaliable without using EHLO\r\n"
1465 << std::flush;
1466 LOG(WARNING) << "STARTTLS issued without using EHLO";
1468 else {
1469 out_() << "220 2.0.0 STARTTLS OK\r\n" << std::flush;
1470 if (sock_.starttls_server(config_path_)) {
1471 reset_();
1472 max_msg_size(Config::max_msg_size_bro);
1473 LOG(INFO) << "STARTTLS " << sock_.tls_info();
1475 else {
1476 LOG(INFO) << "failed STARTTLS";
1481 void Session::exit_()
1483 // sock_.log_totals();
1485 timespec time_used{};
1486 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time_used);
1488 LOG(INFO) << "CPU time " << time_used.tv_sec << "." << std::setw(9)
1489 << std::setfill('0') << time_used.tv_nsec << " seconds";
1491 std::exit(EXIT_SUCCESS);
1494 /////////////////////////////////////////////////////////////////////////////
1496 // All of the verify_* functions send their own error messages back to
1497 // the client on failure, and return false.
1499 bool Session::verify_ip_address_(std::string& error_msg)
1501 auto ip_block_db_name = config_path_ / "ip-block";
1502 CDB ip_block;
1503 if (ip_block.open(ip_block_db_name) &&
1504 ip_block.contains(sock_.them_c_str())) {
1505 error_msg =
1506 fmt::format("IP address {} on static blocklist", sock_.them_c_str());
1507 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1508 return false;
1511 client_fcrdns_.clear();
1513 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1514 (sock_.them_address_literal() == IP6::loopback_literal)) {
1515 LOG(INFO) << "loopback address allowed";
1516 ip_allowed_ = true;
1517 client_fcrdns_.emplace_back("localhost");
1518 client_ = fmt::format("localhost {}", sock_.them_address_literal());
1519 return true;
1522 if (IP::is_private(sock_.them_address_literal())) {
1523 LOG(INFO) << "private address allowed";
1524 ip_allowed_ = true;
1525 client_ = sock_.them_address_literal();
1526 return true;
1529 auto const fcrdns = DNS::fcrdns(res_, sock_.them_c_str());
1530 for (auto const& fcr : fcrdns) {
1531 client_fcrdns_.emplace_back(fcr);
1534 if (!client_fcrdns_.empty()) {
1535 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1536 sock_.them_address_literal());
1537 // check allow list
1538 for (auto const& client_fcrdns : client_fcrdns_) {
1539 if (allow_.contains(client_fcrdns.ascii())) {
1540 LOG(INFO) << "FCrDNS " << client_fcrdns << " allowed";
1541 fcrdns_allowed_ = true;
1542 return true;
1544 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1545 if (tld) {
1546 if (allow_.contains(tld)) {
1547 LOG(INFO) << "FCrDNS registered domain " << tld << " allowed";
1548 fcrdns_allowed_ = true;
1549 return true;
1553 // check blocklist
1554 for (auto const& client_fcrdns : client_fcrdns_) {
1555 if (block_.contains(client_fcrdns.ascii())) {
1556 error_msg =
1557 fmt::format("FCrDNS {} on static blocklist", client_fcrdns.ascii());
1558 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1559 return false;
1562 auto const tld{tld_db_.get_registered_domain(client_fcrdns.ascii())};
1563 if (tld) {
1564 if (block_.contains(tld)) {
1565 error_msg = fmt::format(
1566 "FCrDNS registered domain {} on static blocklist", tld);
1567 out_() << "554 5.7.1 blocklisted\r\n" << std::flush;
1568 return false;
1573 else {
1574 client_ = fmt::format("{}", sock_.them_address_literal());
1577 if (IP4::is_address(sock_.them_c_str())) {
1579 auto const reversed{IP4::reverse(sock_.them_c_str())};
1582 // Check with allow list.
1583 std::shuffle(std::begin(Config::wls), std::end(Config::wls),
1584 random_device_);
1586 for (auto wl : Config::wls) {
1587 DNS::Query q(res_, DNS::RR_type::A, reversed + wl);
1588 if (q.has_record()) {
1589 using namespace boost::xpressive;
1591 auto const as = q.get_strings()[0];
1592 LOG(INFO) << "on allow list " << wl << " as " << as;
1594 mark_tag x_(1);
1595 mark_tag y_(2);
1596 sregex const rex = as_xpr("127.0.") >> (x_ = +_d) >> '.' >> (y_ = +_d);
1597 smatch what;
1599 if (regex_match(as, what, rex)) {
1600 auto const x = what[x_].str();
1601 auto const y = what[y_].str();
1603 int value = 0;
1604 std::from_chars(y.data(), y.data() + y.size(), value);
1605 if (value > 0) {
1606 ip_allowed_ = true;
1607 LOG(INFO) << "allowed";
1611 LOG(INFO) << "Any A record skips check on block list";
1612 return true;
1617 // Check with block lists. <https://en.wikipedia.org/wiki/DNSBL>
1618 std::shuffle(std::begin(Config::bls), std::end(Config::bls),
1619 random_device_);
1621 for (auto bl : Config::bls) {
1623 DNS::Query q(res_, DNS::RR_type::A, reversed + bl);
1624 if (q.has_record()) {
1625 auto const as = q.get_strings()[0];
1626 if (as == "127.0.1.1") {
1627 LOG(INFO) << "Query blocked by " << bl;
1629 else if (as == "127.255.255.252") {
1630 LOG(INFO) << "Typing error in DNSBL name " << bl;
1632 else if (as == "127.255.255.254") {
1633 LOG(INFO) << "Anonymous query through public resolver " << bl;
1635 else if (as == "127.255.255.255") {
1636 LOG(INFO) << "Excessive number of queries " << bl;
1638 else {
1639 error_msg = fmt::format("IP address {} blocked: {} returned {}",
1640 sock_.them_c_str(), bl, as);
1641 out_() << "554 5.7.1 " << error_msg << "\r\n" << std::flush;
1642 return false;
1646 // LOG(INFO) << "IP address " << sock_.them_c_str() << " cleared by dnsbls";
1649 LOG(INFO) << "IP address okay";
1650 return true;
1653 // check the identity from HELO/EHLO
1654 bool Session::verify_client_(Domain const& client_identity,
1655 std::string& error_msg)
1657 if (!client_fcrdns_.empty()) {
1658 if (auto id = std::find(begin(client_fcrdns_), end(client_fcrdns_),
1659 client_identity);
1660 id != end(client_fcrdns_)) {
1661 // If the HELO ident is one of the FCrDNS names...
1662 if (id != begin(client_fcrdns_)) {
1663 // ...then rotate that one to the front of the list
1664 std::rotate(begin(client_fcrdns_), id, id + 1);
1666 client_ = fmt::format("{} {}", client_fcrdns_.front().ascii(),
1667 sock_.them_address_literal());
1668 return true;
1670 LOG(INFO) << "claimed identity " << client_identity
1671 << " does NOT match any FCrDNS: ";
1672 for (auto const& client_fcrdns : client_fcrdns_) {
1673 LOG(INFO) << " " << client_fcrdns;
1677 // Bogus clients claim to be us or some local host.
1678 if (sock_.has_peername() && ((client_identity == server_identity_) ||
1679 (client_identity == "localhost") ||
1680 (client_identity == "localhost.localdomain"))) {
1682 if ((sock_.them_address_literal() == IP4::loopback_literal) ||
1683 (sock_.them_address_literal() == IP6::loopback_literal)) {
1684 return true;
1687 // Give 'em a pass.
1688 if (ip_allowed_) {
1689 LOG(INFO) << "allow-listed IP address can claim to be "
1690 << client_identity;
1691 return true;
1694 // Ease up in test mode.
1695 if (FLAGS_test_mode || getenv("GHSMTP_TEST_MODE")) {
1696 return true;
1699 error_msg = fmt::format("liar, claimed to be {}", client_identity.ascii());
1700 out_() << "550 5.7.1 liar\r\n" << std::flush;
1701 return false;
1704 std::vector<std::string> labels;
1705 boost::algorithm::split(labels, client_identity.ascii(),
1706 boost::algorithm::is_any_of("."));
1707 if (labels.size() < 2) {
1708 error_msg =
1709 fmt::format("claimed bogus identity {}", client_identity.ascii());
1710 out_() << "550 4.7.1 bogus identity\r\n" << std::flush;
1711 return false;
1712 // // Sometimes we may want to look at mail from non conforming
1713 // // sending systems.
1714 // LOG(WARNING) << "invalid sender" << (sock_.has_peername() ? " " : "")
1715 // << client_ << " claiming " << client_identity;
1716 // return true;
1719 if (lookup_domain(block_, client_identity)) {
1720 error_msg =
1721 fmt::format("claimed blocked identity {}", client_identity.ascii());
1722 out_() << "550 4.7.1 blocked identity\r\n" << std::flush;
1723 return false;
1726 auto const tld{tld_db_.get_registered_domain(client_identity.ascii())};
1727 if (!tld) {
1728 // Sometimes we may want to look at mail from misconfigured
1729 // sending systems.
1730 // LOG(WARNING) << "claimed identity has no registered domain";
1731 // return true;
1733 else if (block_.contains(tld)) {
1734 error_msg =
1735 fmt::format("claimed identity has blocked registered domain {}", tld);
1736 out_() << "550 4.7.1 blocked registered domain\r\n" << std::flush;
1737 return false;
1740 // not otherwise objectionable
1741 return true;
1744 // check sender from RFC5321 MAIL FROM:
1745 bool Session::verify_sender_(Mailbox const& sender, std::string& error_msg)
1747 std::string const sender_str{sender};
1749 auto bad_senders_db_name = config_path_ / "bad_senders";
1750 CDB bad_senders;
1751 if (bad_senders.open(bad_senders_db_name) &&
1752 bad_senders.contains(sender_str)) {
1753 error_msg = fmt::format("{} bad sender", sender_str);
1754 out_() << "550 5.1.8 " << error_msg << "\r\n" << std::flush;
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 do_spf_check_(sender);
1787 if (!verify_sender_domain_(sender.domain(), error_msg)) {
1788 return false;
1791 return true;
1794 // this sender is the RFC5321 MAIL FROM: domain part
1795 bool Session::verify_sender_domain_(Domain const& sender,
1796 std::string& error_msg)
1798 if (sender.empty()) {
1799 // MAIL FROM:<>
1800 // is used to send bounce messages.
1801 return true;
1804 // Break sender domain into labels:
1806 std::vector<std::string> labels;
1807 boost::algorithm::split(labels, sender.ascii(),
1808 boost::algorithm::is_any_of("."));
1810 if (labels.size() < 2) { // This is not a valid domain.
1811 error_msg = fmt::format("{} invalid syntax", sender.ascii());
1812 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1813 return false;
1816 if (lookup_domain(block_, sender)) {
1817 error_msg =
1818 fmt::format("SPF sender domain ({}) is blocked", spf_sender_domain_);
1819 out_() << "550 5.7.1 " << error_msg << "\r\n" << std::flush;
1820 return false;
1823 if (spf_result_ == SPF::Result::PASS) {
1824 if (allow_.contains(spf_sender_domain_.ascii())) {
1825 LOG(INFO) << "sender " << spf_sender_domain_.ascii() << " allowed";
1826 return true;
1829 auto const reg_dom{
1830 tld_db_.get_registered_domain(spf_sender_domain_.ascii())};
1831 if (reg_dom) {
1832 if (allow_.contains(reg_dom)) {
1833 LOG(INFO) << "sender registered domain \"" << reg_dom << "\" allowed";
1834 return true;
1839 LOG(INFO) << "sender \"" << sender << "\" not disallowed";
1840 return true;
1843 void Session::do_spf_check_(Mailbox const& sender)
1845 if (!sock_.has_peername()) {
1846 auto const ip_addr = "127.0.0.1"; // use localhost for local socket
1847 spf_received_ =
1848 fmt::format("Received-SPF: pass ({}: allow-listed) client-ip={}; "
1849 "envelope-from={}; helo={};",
1850 server_id_(), ip_addr, sender, client_identity_);
1851 spf_sender_domain_ = "localhost";
1852 return;
1855 auto const spf_srv = SPF::Server{server_id_().c_str()};
1856 auto spf_request = SPF::Request{spf_srv};
1858 if (IP4::is_address(sock_.them_c_str())) {
1859 spf_request.set_ipv4_str(sock_.them_c_str());
1861 else if (IP6::is_address(sock_.them_c_str())) {
1862 spf_request.set_ipv6_str(sock_.them_c_str());
1864 else {
1865 LOG(FATAL) << "bogus address " << sock_.them_address_literal() << ", "
1866 << sock_.them_c_str();
1869 auto const from{static_cast<std::string>(sender)};
1871 spf_request.set_env_from(from.c_str());
1872 spf_request.set_helo_dom(client_identity_.ascii().c_str());
1874 auto const spf_res{SPF::Response{spf_request}};
1875 spf_result_ = spf_res.result();
1876 spf_received_ = spf_res.received_spf();
1877 spf_sender_domain_ = spf_request.get_sender_dom();
1879 LOG(INFO) << "spf_received_ == " << spf_received_;
1881 if (spf_result_ == SPF::Result::FAIL) {
1882 LOG(INFO) << "FAIL " << spf_res.header_comment();
1884 else if (spf_result_ == SPF::Result::NEUTRAL) {
1885 LOG(INFO) << "NEUTRAL " << spf_res.header_comment();
1887 else if (spf_result_ == SPF::Result::PASS) {
1888 LOG(INFO) << "PASS " << spf_res.header_comment();
1890 else {
1891 LOG(INFO) << "INVALID/SOFTFAIL/NONE/xERROR " << spf_res.header_comment();
1895 bool Session::verify_from_params_(parameters_t const& parameters)
1897 // Take a look at the optional parameters:
1898 for (auto const& [name, value] : parameters) {
1899 if (iequal(name, "BODY")) {
1900 if (iequal(value, "8BITMIME")) {
1901 // everything is cool, this is our default...
1903 else if (iequal(value, "7BIT")) {
1904 // nothing to see here, move along...
1906 else if (iequal(value, "BINARYMIME")) {
1907 binarymime_ = true;
1909 else {
1910 LOG(WARNING) << "unrecognized BODY type \"" << value << "\" requested";
1913 else if (iequal(name, "SMTPUTF8")) {
1914 if (!value.empty()) {
1915 LOG(WARNING) << "SMTPUTF8 parameter has a value: " << value;
1917 smtputf8_ = true;
1920 // else if (iequal(name, "PRDR")) {
1921 // LOG(INFO) << "using PRDR";
1922 // prdr_ = true;
1923 // }
1925 else if (iequal(name, "SIZE")) {
1926 if (value.empty()) {
1927 LOG(WARNING) << "SIZE parameter has no value.";
1929 else {
1930 try {
1931 auto const sz = stoull(value);
1932 if (sz > max_msg_size()) {
1933 out_() << "552 5.3.4 message size limit exceeded\r\n" << std::flush;
1934 LOG(WARNING) << "SIZE parameter too large: " << sz;
1935 return false;
1938 catch (std::invalid_argument const& e) {
1939 LOG(WARNING) << "SIZE parameter has invalid value: " << value;
1941 catch (std::out_of_range const& e) {
1942 LOG(WARNING) << "SIZE parameter has out-of-range value: " << value;
1944 // I guess we just ignore bad size parameters.
1947 else if (iequal(name, "REQUIRETLS")) {
1948 if (!sock_.tls()) {
1949 out_() << "554 5.7.1 REQUIRETLS needed\r\n" << std::flush;
1950 LOG(WARNING) << "REQUIRETLS needed";
1951 return false;
1954 else {
1955 LOG(WARNING) << "unrecognized 'MAIL FROM' parameter " << name << "="
1956 << value;
1960 return true;
1963 bool Session::verify_rcpt_params_(parameters_t const& parameters)
1965 // Take a look at the optional parameters:
1966 for (auto const& [name, value] : parameters) {
1967 if (iequal(name, "RRVS")) {
1968 // rrvs-param = "RRVS=" date-time [ ";" ( "C" / "R" ) ]
1969 LOG(INFO) << name << "=" << value;
1971 else {
1972 LOG(WARNING) << "unrecognized 'RCPT TO' parameter " << name << "="
1973 << value;
1977 return true;
1980 // check recipient from RFC5321 RCPT TO:
1981 bool Session::verify_recipient_(Mailbox const& recipient)
1983 if ((recipient.local_part() == "Postmaster") && (recipient.domain() == "")) {
1984 LOG(INFO) << "magic Postmaster address";
1985 return true;
1988 auto const accepted_domain{[this, &recipient] {
1989 if (recipient.domain().is_address_literal()) {
1990 if (recipient.domain() != sock_.us_address_literal()) {
1991 LOG(WARNING) << "recipient.domain address " << recipient.domain()
1992 << " does not match ours " << sock_.us_address_literal();
1993 return false;
1995 return true;
1998 // Domains we accept mail for.
1999 if (accept_domains_.is_open()) {
2000 if (accept_domains_.contains(recipient.domain().ascii()) ||
2001 accept_domains_.contains(recipient.domain().utf8())) {
2002 return true;
2005 else {
2006 // If we have no list of domains to accept, at least take our own.
2007 if (recipient.domain() == server_id_()) {
2008 return true;
2012 return false;
2013 }()};
2015 if (!accepted_domain) {
2016 out_() << "554 5.7.1 relay access denied\r\n" << std::flush;
2017 LOG(WARNING) << "relay access denied for domain " << recipient.domain();
2018 return false;
2021 // Check for local addresses we reject.
2023 auto bad_recipients_db_name = config_path_ / "bad_recipients";
2024 CDB bad_recipients_db;
2025 if (bad_recipients_db.open(bad_recipients_db_name) &&
2026 bad_recipients_db.contains(recipient.local_part())) {
2027 out_() << "550 5.1.1 bad recipient " << recipient << "\r\n" << std::flush;
2028 LOG(WARNING) << "bad recipient " << recipient;
2029 return false;
2034 auto fail_db_name = config_path_ / "fail_554";
2035 if (fs::exists(fail_db_name)) {
2036 CDB fail_db;
2037 if (fail_db.open(fail_db_name) &&
2038 fail_db.contains(recipient.local_part())) {
2039 out_() << "554 5.7.1 prohibited for policy reasons" << recipient
2040 << "\r\n"
2041 << std::flush;
2042 LOG(WARNING) << "fail_554 recipient " << recipient;
2043 return false;
2049 auto temp_fail_db_name = config_path_ / "temp_fail";
2050 CDB temp_fail;
2051 if (temp_fail.open(temp_fail_db_name) &&
2052 temp_fail.contains(recipient.local_part())) {
2053 out_() << "432 4.3.0 recipient's incoming mail queue has been stopped\r\n"
2054 << std::flush;
2055 LOG(WARNING) << "temp fail for recipient " << recipient;
2056 return false;
2060 // Check for and act on magic "wait" address.
2062 using namespace boost::xpressive;
2064 sregex const rex = icase("wait-rcpt-") >> (secs_ = +_d);
2065 smatch what;
2067 if (regex_match(recipient.local_part(), what, rex) ||
2068 regex_match(recipient.local_part(), what, all_rex)) {
2069 auto const str = what[secs_].str();
2070 LOG(INFO) << "waiting at RCPT TO " << str << " seconds";
2071 long value = 0;
2072 std::from_chars(str.data(), str.data() + str.size(), value);
2073 google::FlushLogFiles(google::INFO);
2074 out_() << std::flush;
2075 sleep(value);
2076 LOG(INFO) << "done waiting";
2080 // This is a trap for a probe done by some senders to see if we
2081 // accept just any old local-part.
2082 if (!extensions_) {
2083 if (recipient.local_part().length() > 8) {
2084 out_() << "550 5.1.1 unknown recipient " << recipient << "\r\n"
2085 << std::flush;
2086 LOG(WARNING) << "unknown recipient for HELO " << recipient;
2087 return false;
2091 return true;