Home | History | Annotate | Line # | Download | only in smtp
      1 /*	$NetBSD: smtp_rcpt.c,v 1.4 2026/05/09 18:49:20 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	smtp_rcpt 3
      6 /* SUMMARY
      7 /*	application-specific recipient list operations
      8 /* SYNOPSIS
      9 /*	#include <smtp.h>
     10 /*
     11 /*	SMTP_RCPT_INIT(state)
     12 /*	SMTP_STATE *state;
     13 /*
     14 /*	SMTP_RCPT_DROP(state, rcpt)
     15 /*	SMTP_STATE *state;
     16 /*	RECIPIENT *rcpt;
     17 /*
     18 /*	SMTP_RCPT_KEEP(state, rcpt)
     19 /*	SMTP_STATE *state;
     20 /*	RECIPIENT *rcpt;
     21 /*
     22 /*	SMTP_RCPT_ISMARKED(rcpt)
     23 /*	RECIPIENT *rcpt;
     24 /*
     25 /*	void	smtp_rcpt_cleanup(SMTP_STATE *state)
     26 /*	SMTP_STATE *state;
     27 /*
     28 /*	int	SMTP_RCPT_LEFT(state)
     29 /*	SMTP_STATE *state;
     30 /*
     31 /*	int	SMTP_RCPT_MARK_COUNT(state)
     32 /*	SMTP_STATE *state;
     33 /*
     34 /*	void	smtp_rcpt_done(state, resp, rcpt)
     35 /*	SMTP_STATE *state;
     36 /*	SMTP_RESP *resp;
     37 /*	RECIPIENT *rcpt;
     38 /* DESCRIPTION
     39 /*	This module implements application-specific mark and sweep
     40 /*	operations on recipient lists. Operation is as follows:
     41 /* .IP \(bu
     42 /*	In the course of a delivery attempt each recipient is
     43 /*	marked either as DROP (remove from recipient list) or KEEP
     44 /*	(deliver to alternate mail server).
     45 /* .IP \(bu
     46 /*	After a delivery attempt any recipients marked DROP are deleted
     47 /*	from the request, and the left-over recipients are unmarked.
     48 /* .PP
     49 /*	The mark/sweep algorithm is implemented in a redundant manner,
     50 /*	and ensures that all recipients are explicitly accounted for.
     51 /*
     52 /*	Operations with upper case names are implemented by macros
     53 /*	whose arguments may be evaluated more than once.
     54 /*
     55 /*	SMTP_RCPT_INIT() initializes application-specific recipient
     56 /*	information and must be called before the first delivery attempt.
     57 /*
     58 /*	SMTP_RCPT_DROP() marks the specified recipient as DROP (remove
     59 /*	from recipient list). It is an error to mark an already marked
     60 /*	recipient.
     61 /*
     62 /*	SMTP_RCPT_KEEP() marks the specified recipient as KEEP (deliver
     63 /*	to alternate mail server). It is an error to mark an already
     64 /*	marked recipient.
     65 /*
     66 /*	SMTP_RCPT_ISMARKED() returns non-zero when the specified
     67 /*	recipient is marked.
     68 /*
     69 /*	SMTP_RCPT_LEFT() returns the number of left_over recipients
     70 /*	(the total number of marked and non-marked recipients).
     71 /*
     72 /*	SMTP_RCPT_MARK_COUNT() returns the number of left_over
     73 /*	recipients that are marked.
     74 /*
     75 /*	smtp_rcpt_cleanup() cleans up the in-memory recipient list.
     76 /*	It removes the recipients marked DROP from the left-over
     77 /*	recipients, unmarks the left-over recipients, and enforces
     78 /*	the requirement that all recipients are marked upon entry.
     79 /*
     80 /*	smtp_rcpt_done() logs that a recipient is completed and upon
     81 /*	success it marks the recipient as done in the queue file.
     82 /*	Finally, it marks the in-memory recipient as DROP.
     83 /*
     84 /*	Note: smtp_rcpt_done() may change the order of the recipient
     85 /*	list.
     86 /* DIAGNOSTICS
     87 /*	Panic: interface violation.
     88 /*
     89 /*	When a recipient can't be logged as completed, the recipient is
     90 /*	logged as deferred instead.
     91 /* BUGS
     92 /*	The single recipient list abstraction dates from the time
     93 /*	that the SMTP client would give up after one SMTP session,
     94 /*	so that each recipient was either bounced, delivered or
     95 /*	deferred. Implicitly, all recipients were marked as DROP.
     96 /*
     97 /*	This abstraction is less convenient when an SMTP client
     98 /*	must be able to deliver left-over recipients to a backup
     99 /*	host. It might be more natural to have an input list with
    100 /*	recipients to deliver, and an output list with left-over
    101 /*	recipients.
    102 /* LICENSE
    103 /* .ad
    104 /* .fi
    105 /*	The Secure Mailer license must be distributed with this software.
    106 /* AUTHOR(S)
    107 /*	Wietse Venema
    108 /*	IBM T.J. Watson Research
    109 /*	P.O. Box 704
    110 /*	Yorktown Heights, NY 10598, USA
    111 /*
    112 /*	Wietse Venema
    113 /*	Google, Inc.
    114 /*	111 8th Avenue
    115 /*	New York, NY 10011, USA
    116 /*--*/
    117 
    118 /* System  library. */
    119 
    120 #include <sys_defs.h>
    121 #include <stdlib.h>			/* smtp_rcpt_cleanup  */
    122 #include <string.h>
    123 
    124 /* Utility  library. */
    125 
    126 #include <msg.h>
    127 #include <stringops.h>
    128 #include <mymalloc.h>
    129 
    130 /* Global library. */
    131 
    132 #include <mail_params.h>
    133 #include <deliver_request.h>		/* smtp_rcpt_done */
    134 #include <deliver_completed.h>		/* smtp_rcpt_done */
    135 #include <sent.h>			/* smtp_rcpt_done */
    136 #include <dsn_mask.h>			/* smtp_rcpt_done */
    137 
    138 /* Application-specific. */
    139 
    140 #include <smtp.h>
    141 
    142 /* smtp_rcpt_done - mark recipient as done or else */
    143 
    144 void    smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
    145 {
    146     DELIVER_REQUEST *request = state->request;
    147     SMTP_SESSION *session = state->session;
    148     SMTP_ITERATOR *iter = state->iterator;
    149     DSN_BUF *why = state->why;
    150     const char *dsn_action = "relayed";
    151     int     status;
    152 
    153     /*
    154      * Assume this was intermediate delivery when the server announced DSN
    155      * support, and don't send a DSN "SUCCESS" notification.
    156      */
    157     if (session->features & SMTP_FEATURE_DSN)
    158 	rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;
    159 
    160     /*
    161      * Assume this was final delivery when the LMTP server announced no DSN
    162      * support. In backwards compatibility mode, send a "relayed" instead of
    163      * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify"
    164      * the expression. The redundancy is for clarity. It is trivially
    165      * eliminated by the compiler. There is no need to sacrifice clarity for
    166      * the sake of "performance".
    167      */
    168     if ((session->features & SMTP_FEATURE_DSN) == 0
    169 	&& !smtp_mode
    170 	&& (smtp_cli_attr.flags & SMTP_CLI_FLAG_FINAL_DELIVERY) != 0)
    171 	dsn_action = "delivered";
    172 
    173     /*
    174      * Report success and delete the recipient from the delivery request.
    175      * Defer if the success can't be reported.
    176      *
    177      * Note: the DSN action is ignored in case of address probes.
    178      */
    179     dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
    180 	       DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
    181 
    182     status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
    183 		  request->queue_id, &request->msg_stats, rcpt,
    184 		  session->namaddrport, state->tls_stats,
    185 		  DSN_FROM_DSN_BUF(why));
    186     if (status == 0)
    187 	if (request->flags & DEL_REQ_FLAG_SUCCESS)
    188 	    deliver_completed(state->src, rcpt->offset);
    189     SMTP_RCPT_DROP(state, rcpt);
    190     state->status |= status;
    191 }
    192 
    193 /* smtp_rcpt_cleanup_callback - qsort callback */
    194 
    195 static int smtp_rcpt_cleanup_callback(const void *a, const void *b)
    196 {
    197     return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status);
    198 }
    199 
    200 /* smtp_rcpt_cleanup - purge completed recipients from request */
    201 
    202 void    smtp_rcpt_cleanup(SMTP_STATE *state)
    203 {
    204     RECIPIENT_LIST *rcpt_list = &state->request->rcpt_list;
    205     RECIPIENT *rcpt;
    206 
    207     /*
    208      * Sanity checks.
    209      */
    210     if (state->rcpt_drop + state->rcpt_keep != state->rcpt_left)
    211 	msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d",
    212 		  state->rcpt_drop, state->rcpt_keep, state->rcpt_left);
    213 
    214     /*
    215      * Recipients marked KEEP sort before recipients marked DROP. Skip the
    216      * sorting in the common case that all recipients are marked the same.
    217      */
    218     if (state->rcpt_drop > 0 && state->rcpt_keep > 0)
    219 	qsort((void *) rcpt_list->info, state->rcpt_left,
    220 	      sizeof(rcpt_list->info[0]), smtp_rcpt_cleanup_callback);
    221 
    222     /*
    223      * Truncate the recipient list and unmark the left-over recipients.
    224      */
    225     state->rcpt_left = state->rcpt_keep;
    226     for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++)
    227 	rcpt->u.status = 0;
    228     state->rcpt_drop = state->rcpt_keep = 0;
    229 }
    230