1 /* $NetBSD: smtp_rcpt.c,v 1.3 2020/03/18 19:05: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
smtp_rcpt_done(SMTP_STATE * state,SMTP_RESP * resp,RECIPIENT * rcpt)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, DSN_FROM_DSN_BUF(why));
185 if (status == 0)
186 if (request->flags & DEL_REQ_FLAG_SUCCESS)
187 deliver_completed(state->src, rcpt->offset);
188 SMTP_RCPT_DROP(state, rcpt);
189 state->status |= status;
190 }
191
192 /* smtp_rcpt_cleanup_callback - qsort callback */
193
smtp_rcpt_cleanup_callback(const void * a,const void * b)194 static int smtp_rcpt_cleanup_callback(const void *a, const void *b)
195 {
196 return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status);
197 }
198
199 /* smtp_rcpt_cleanup - purge completed recipients from request */
200
smtp_rcpt_cleanup(SMTP_STATE * state)201 void smtp_rcpt_cleanup(SMTP_STATE *state)
202 {
203 RECIPIENT_LIST *rcpt_list = &state->request->rcpt_list;
204 RECIPIENT *rcpt;
205
206 /*
207 * Sanity checks.
208 */
209 if (state->rcpt_drop + state->rcpt_keep != state->rcpt_left)
210 msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d",
211 state->rcpt_drop, state->rcpt_keep, state->rcpt_left);
212
213 /*
214 * Recipients marked KEEP sort before recipients marked DROP. Skip the
215 * sorting in the common case that all recipients are marked the same.
216 */
217 if (state->rcpt_drop > 0 && state->rcpt_keep > 0)
218 qsort((void *) rcpt_list->info, state->rcpt_left,
219 sizeof(rcpt_list->info[0]), smtp_rcpt_cleanup_callback);
220
221 /*
222 * Truncate the recipient list and unmark the left-over recipients.
223 */
224 state->rcpt_left = state->rcpt_keep;
225 for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++)
226 rcpt->u.status = 0;
227 state->rcpt_drop = state->rcpt_keep = 0;
228 }
229