The following diff introduces the junk action which allows builtin filters to junk a session or transaction.
The following would junk a session if it doesn't have rdns when it reaches the helo filtering hook: match "check-rdns" phase helo match !rdns junk Currently this is not doable so builtin filters may only accept or throw away a session or transaction, whereas with this diff you'll be able to automatically classify in Junk folder with the junk mda option: action "local_users" maildir junk This also allows simplifying proc filters that want to junk as the current method requires registering for data-line and injecting an X-Spam: Yes header, while this can now be handled by returning the junk filter response at any phase and letting smtpd prefill header Index: lka_filter.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/lka_filter.c,v retrieving revision 1.44 diff -u -p -r1.44 lka_filter.c --- lka_filter.c 29 Aug 2019 08:49:55 -0000 1.44 +++ lka_filter.c 2 Sep 2019 20:04:37 -0000 @@ -56,6 +56,7 @@ static int filter_builtins_mail_from(str static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *); static void filter_result_proceed(uint64_t); +static void filter_result_junk(uint64_t); static void filter_result_rewrite(uint64_t, const char *); static void filter_result_reject(uint64_t, const char *); static void filter_result_disconnect(uint64_t, const char *); @@ -479,6 +480,11 @@ lka_filter_process_response(const char * fatalx("Unexpected parameter after proceed: %s", line); filter_protocol_next(token, reqid, 0); return; + } else if (strcmp(response, "junk") == 0) { + if (parameter != NULL) + fatalx("Unexpected parameter after junk: %s", line); + filter_result_junk(reqid); + return; } else { if (parameter == NULL) fatalx("Missing parameter: %s", line); @@ -574,6 +580,15 @@ filter_protocol_internal(struct filter_s filter_result_disconnect(reqid, filter->config->disconnect); return; } + else if (filter->config->junk) { + log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " + "resume=%s, action=junk, filter=%s, query=%s", + fs->id, phase_name, resume ? "y" : "n", + filter->name, + param); + filter_result_junk(reqid); + return; + } else { log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " "resume=%s, action=reject, filter=%s, query=%s, response=%s", @@ -759,6 +774,15 @@ filter_result_proceed(uint64_t reqid) m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); m_add_id(p_pony, reqid); m_add_int(p_pony, FILTER_PROCEED); + m_close(p_pony); +} + +static void +filter_result_junk(uint64_t reqid) +{ + m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); + m_add_id(p_pony, reqid); + m_add_int(p_pony, FILTER_JUNK); m_close(p_pony); } Index: lka_report.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/lka_report.c,v retrieving revision 1.27 diff -u -p -r1.27 lka_report.c --- lka_report.c 29 Aug 2019 07:23:18 -0000 1.27 +++ lka_report.c 2 Sep 2019 20:04:38 -0000 @@ -428,6 +428,9 @@ lka_report_smtp_filter_response(const ch case FILTER_PROCEED: response_name = "proceed"; break; + case FILTER_JUNK: + response_name = "junk"; + break; case FILTER_REWRITE: response_name = "rewrite"; break; Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v retrieving revision 1.259 diff -u -p -r1.259 parse.y --- parse.y 25 Aug 2019 03:40:45 -0000 1.259 +++ parse.y 2 Sep 2019 20:04:38 -0000 @@ -1297,6 +1297,9 @@ REJECT STRING { | REWRITE STRING { filter_config->rewrite = $2; } +| JUNK { + filter_config->junk = 1; +} ; filter_phase_check_fcrdns: Index: smtp_session.c =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtp_session.c,v retrieving revision 1.408 diff -u -p -r1.408 smtp_session.c --- smtp_session.c 28 Aug 2019 15:50:36 -0000 1.408 +++ smtp_session.c 2 Sep 2019 20:04:39 -0000 @@ -126,6 +126,8 @@ struct smtp_tx { int rcvcount; int has_date; int has_message_id; + + uint8_t junk; }; struct smtp_session { @@ -154,6 +156,8 @@ struct smtp_session { enum smtp_command last_cmd; enum filter_phase filter_phase; const char *filter_param; + + uint8_t junk; }; #define ADVERTISE_TLS(s) \ @@ -970,7 +974,8 @@ smtp_session_imsg(struct mproc *p, struc m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_int(&m, &filter_response); - if (filter_response != FILTER_PROCEED) + if (filter_response != FILTER_PROCEED && + filter_response != FILTER_JUNK) m_get_string(&m, &filter_param); else filter_param = NULL; @@ -998,9 +1003,17 @@ smtp_session_imsg(struct mproc *p, struc smtp_proceed_rollback(s, NULL); break; + + case FILTER_JUNK: + if (s->tx) + s->tx->junk = 1; + else + s->junk = 1; + /* fallthrough */ + case FILTER_PROCEED: filter_param = s->filter_param; - /* fallthrough*/ + /* fallthrough */ case FILTER_REWRITE: report_smtp_filter_response("smtp-in", s->id, s->filter_phase, @@ -2815,6 +2828,9 @@ smtp_message_begin(struct smtp_tx *tx) smtp_reply(s, "354 Enter mail, end with \".\"" " on a line by itself"); + if (s->junk || (s->tx && s->tx->junk)) + m_printf(tx, "X-Spam: Yes\n"); + m_printf(tx, "Received: "); if (!(s->listener->flags & F_MASK_SOURCE)) { m_printf(tx, "from %s (%s [%s])", Index: smtpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.conf.5,v retrieving revision 1.221 diff -u -p -r1.221 smtpd.conf.5 --- smtpd.conf.5 17 Aug 2019 14:43:21 -0000 1.221 +++ smtpd.conf.5 2 Sep 2019 20:04:39 -0000 @@ -348,6 +348,15 @@ to disconnect session with Phase and matching conditions are documented in a specific section, see .Sx BUILTIN FILTERING . +.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic junk +Register builtin filter +.Ar filter-name +matching +.Ar conditions +to mark a session or a transaction as junk. +Phase and matching conditions are documented in a specific section, +see +.Sx BUILTIN FILTERING . .It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic reject Ar message Register builtin filter .Ar filter-name Index: smtpd.h =================================================================== RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v retrieving revision 1.633 diff -u -p -r1.633 smtpd.h --- smtpd.h 28 Aug 2019 15:50:36 -0000 1.633 +++ smtpd.h 2 Sep 2019 20:04:40 -0000 @@ -1046,6 +1046,7 @@ struct filter_config { char *reject; char *disconnect; char *rewrite; + uint8_t junk; char *proc; const char **chain; @@ -1095,6 +1096,7 @@ enum filter_status { FILTER_REWRITE, FILTER_REJECT, FILTER_DISCONNECT, + FILTER_JUNK, }; enum ca_resp_status {