kumo.generate_rfc3464_message
Since: Version 2025.10.06-5ec871ab
The functionality described in this section requires version 2025.10.06-5ec871ab of KumoMTA, or a more recent version.
Generates an RFC 3464 delivery status Message from a log
record. This function is intended to be used from inside a log_disposition
event handler to facilitate the generation of bounce messages.
For sender-oriented use cases we generally recommend against generating bounce messages as they place additional load on the system and introduce some degree of Backscatter risk.
When KumoMTA is deployed as a more general purpose relay it becomes more desirable to generate delivery status reports to the originating user, which is where this function comes in.
PARAMS is an object style table with the following fields:
include_original_messageis a string that can be one ofFullContent,HeadersOnlyorNo. This controls whether the entire message, just its headers, or none of the message that triggered the report should be included within the report. When set to anything other thanNo, theOPT_ORIG_MSGparameter must also be passed tokumo.generate_rfc3464_message.enable_expirationis an optional boolean value that defaults tofalseif not specified. Whentrue, a report message will be generated for messages that have exceeded their configured time in the queue without delivery.enable_bounceis an optional boolean value that defaults tofalseif not specified. Whentrue, a report message will be generated for messages that experience a permanent failure response when talking to the next hop MTA.reporting_mtais a required lua table with the fieldsmta_typeandnamethat will be included in theReporting-MTAheader of the generated report.mta_typewill typically bednsandnamewill typically be the corresponding DNS name that identifies the current host generating the report. The domain name will appear in theFromheader of the generated report as well.stable_contentis an optional boolean value that defaults tofalseif not specified. Whentrue, various header fields will be set to constant values that will make the generated message easier to reason about in a test harness. You will not typically need to use this parameter.
The OPT_ORIG_MSG parameter is an optional Message that
will used to provide the original message content in the report.
LOG_RECORD is a JsonLogRecord describing the event that
occurred to the message.
kumo.generate_rfc3464_message takes those three parameters and produces an
optional Message from them.
The return value may be nil, for example, if the LOG_RECORD doesn't match
any of the types that are enabled in the PARAMS, or if there is some
information missing or otherwise inapplicable; for example, if LOG_RECORD is
a Bounce but for a protocol other than ESMTP then no report will be
generated.
Example of generating non-delivery reports
local log_hooks = require 'policy-extras.log_hooks'
local function ndr_generator(msg, log_record)
local params = {
include_original_message = 'FullContent',
enable_expiration = true,
enable_bounce = true,
reporting_mta = {
mta_type = 'dns',
name = 'mta1.example.com',
},
}
local bounce_msg = kumo.generate_rfc3464_message(params, msg, log_record)
if bounce_msg then
local ok, err = pcall(kumo.inject_message, bounce_msg)
if not ok then
kumo.log_error('failed to inject NDR: ', err)
end
end
end
log_hooks:new_disposition_hook {
name = 'ndr_generator',
hook = ndr_generator,
}
If you wish to customize the human readable portion of the message, you might consider using the mime parsing functions:
local mime = bounce_msg:parse_mime()
local structure = mime:get_simple_structure()
structure.text_part.body = 'MODIFIED!\r\n' .. structure.text_part.body
bounce_msg:set_data(tostring(mime))
Sample of a generated bounce report
Content-Type: multipart/report;
boundary="report-boundary";
report-type="delivery-status"
Subject: Returned mail
Mime-Version: 1.0
Message-ID: <UUID@mta1.example.com>
To: sender@sender.example.com
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>
--report-boundary
Content-Type: text/plain;
charset="us-ascii"
The message was received at Tue, 1 Jul 2003 08:52:37 +0000
from sender@sender.example.com and addressed to recip@target.example.com.
While communicating with target.example.com (42.42.42.42):
Response: 550 5.7.1 no thanks
The message will be deleted from the queue.
No further attempts will be made to deliver it.
--report-boundary
Content-Type: message/delivery-status;
charset="us-ascii"
Reporting-MTA: dns; mta1.example.com
Arrival-Date: Tue, 1 Jul 2003 08:52:37 +0000
Final-Recipient: rfc822;recip@target.example.com
Action: failed
Status: 5.7.1 no thanks
Remote-MTA: dns; target.example.com
Diagnostic-Code: smtp; 550 5.7.1 no thanks
Last-Attempt-Date: Tue, 1 Jul 2003 10:52:37 +0000
--report-boundary
Content-Type: message/rfc822
Content-Type: text/plain;
charset="us-ascii"
Subject: Hello!
hello there
--report-boundary--
Sample of a generated expiration report
Content-Type: multipart/report;
boundary="report-boundary";
report-type="delivery-status"
Subject: Returned mail
Mime-Version: 1.0
Message-ID: <UUID@mta1.example.com>
To: sender@sender.example.com
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>
--report-boundary
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
The message was received at Tue, 1 Jul 2003 08:52:37 +0000
from sender@sender.example.com and addressed to recip@target.example.com.
Status: 551 5.4.7 Next delivery time would be at SOME TIME which exceeds th=
e expiry time EXPIRES configured via set_scheduling
The message will be deleted from the queue.
No further attempts will be made to deliver it.
--report-boundary
Content-Type: message/delivery-status;
charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Reporting-MTA: dns; mta1.example.com
Arrival-Date: Tue, 1 Jul 2003 08:52:37 +0000
Final-Recipient: rfc822;recip@target.example.com
Action: failed
Status: 5.4.7 Next delivery time would be at SOME TIME which exceeds the ex=
piry time EXPIRES configured via set_scheduling
Diagnostic-Code: smtp; 551 5.4.7 Next delivery time would be at SOME TIME w=
hich exceeds the expiry time EXPIRES configured via set_scheduling
Last-Attempt-Date: Tue, 1 Jul 2003 10:52:37 +0000
--report-boundary
Content-Type: text/rfc822-headers
Content-Type: text/plain;
charset="us-ascii"
Subject: Hello!
--report-boundary--