Skip to content

kumo.generate_rfc3464_message

local bounce_msg =
  kumo.generate_rfc3464_message(PARAMS, OPT_ORIG_MSG, LOG_RECORD)
Since: Dev Builds Only

The functionality described in this section requires a dev build of KumoMTA. You can obtain a dev build by following the instructions in the Installation section.

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_message is a string that can be one of FullContent, HeadersOnly or No. 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 than No, the OPT_ORIG_MSG parameter must also be passed to kumo.generate_rfc3464_message.
  • enable_expiration is an optional boolean value that defaults to false if not specified. When true, a report message will be generated for messages that have exceeded their configured time in the queue without delivery.
  • enable_bounce is an optional boolean value that defaults to false if not specified. When true, a report message will be generated for messages that experience a permanent failure response when talking to the next hop MTA.
  • reporting_mta is a required lua table with the fields mta_type and name that will be included in the Reporting-MTA header of the generated report. mta_type will typically be dns and name will typically be the corresponding DNS name that identifies the current host generating the report. The domain name will appear in the From header of the generated report as well.
  • stable_content is an optional boolean value that defaults to false if not specified. When true, 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'

log_hooks:new_disposition_hook {
  name = 'ndr_generator',
  hook = ndr_generator,
}

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

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--