1 /* $NetBSD: bounce.c,v 1.4 2025/02/25 19:15:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* bounce 3
6 /* SUMMARY
7 /* bounce service client
8 /* SYNOPSIS
9 /* #include <bounce.h>
10 /*
11 /* int bounce_append(flags, id, stats, recipient, relay, dsn)
12 /* int flags;
13 /* const char *id;
14 /* MSG_STATS *stats;
15 /* RECIPIENT *rcpt;
16 /* const char *relay;
17 /* DSN *dsn;
18 /*
19 /* int bounce_flush(flags, queue, id, encoding, sendopts, sender,
20 /* dsn_envid, dsn_ret)
21 /* int flags;
22 /* const char *queue;
23 /* const char *id;
24 /* const char *encoding;
25 /* int sendopts;
26 /* const char *sender;
27 /* const char *dsn_envid;
28 /* int dsn_ret;
29 /*
30 /* int bounce_flush_verp(flags, queue, id, encoding, sendopts,
31 /* sender, dsn_envid, dsn_ret, verp_delims)
32 /* int flags;
33 /* const char *queue;
34 /* const char *id;
35 /* const char *encoding;
36 /* int sendopts;
37 /* const char *sender;
38 /* const char *dsn_envid;
39 /* int dsn_ret;
40 /* const char *verp_delims;
41 /*
42 /* int bounce_one(flags, queue, id, encoding, sendopts, sender,
43 /* dsn_envid, ret, stats, recipient, relay, dsn)
44 /* int flags;
45 /* const char *queue;
46 /* const char *id;
47 /* const char *encoding;
48 /* int sendopts;
49 /* const char *sender;
50 /* const char *dsn_envid;
51 /* int dsn_ret;
52 /* MSG_STATS *stats;
53 /* RECIPIENT *rcpt;
54 /* const char *relay;
55 /* DSN *dsn;
56 /*
57 /* void bounce_client_init(title, maps)
58 /* const char *title;
59 /* const char *maps;
60 /* INTERNAL API
61 /* DSN_FILTER *delivery_status_filter;
62 /*
63 /* int bounce_append_intern(flags, id, stats, recipient, relay, dsn)
64 /* int flags;
65 /* const char *id;
66 /* MSG_STATS *stats;
67 /* RECIPIENT *rcpt;
68 /* const char *relay;
69 /*
70 /* int bounce_one_intern(flags, queue, id, encoding, sendopts, sender,
71 /* dsn_envid, ret, stats, recipient, relay, dsn)
72 /* int flags;
73 /* const char *queue;
74 /* const char *id;
75 /* const char *encoding;
76 /* int sendopts;
77 /* const char *sender;
78 /* const char *dsn_envid;
79 /* int dsn_ret;
80 /* MSG_STATS *stats;
81 /* RECIPIENT *rcpt;
82 /* const char *relay;
83 /* DSN *dsn;
84 /* DESCRIPTION
85 /* This module implements the client interface to the message
86 /* bounce service, which maintains a per-message log of status
87 /* records with recipients that were bounced, and the dsn_text why.
88 /*
89 /* bounce_append() appends a dsn_text for non-delivery to the
90 /* bounce log for the named recipient, updates the address
91 /* verification service, or updates a message delivery record
92 /* on request by the sender. The flags argument determines
93 /* the action.
94 /*
95 /* bounce_flush() actually bounces the specified message to
96 /* the specified sender, including the bounce log that was
97 /* built with bounce_append(). The bounce logfile is removed
98 /* upon successful completion.
99 /*
100 /* bounce_flush_verp() is like bounce_flush(), but sends one
101 /* notification per recipient, with the failed recipient encoded
102 /* into the sender address.
103 /*
104 /* bounce_one() bounces one recipient and immediately sends a
105 /* notification to the sender. This procedure does not append
106 /* the recipient and dsn_text to the per-message bounce log, and
107 /* should be used when a delivery agent changes the error
108 /* return address in a manner that depends on the recipient
109 /* address.
110 /*
111 /* bounce_client_init() initializes an optional DSN filter.
112 /*
113 /* bounce_append_intern() and bounce_one_intern() are for use
114 /* after the DSN filter.
115 /*
116 /* Arguments:
117 /* .IP flags
118 /* The bitwise OR of zero or more of the following (specify
119 /* BOUNCE_FLAG_NONE to request no special processing):
120 /* .RS
121 /* .IP BOUNCE_FLAG_CLEAN
122 /* Delete the bounce log in case of an error (as in: pretend
123 /* that we never even tried to bounce this message).
124 /* .IP BOUNCE_FLAG_DELRCPT
125 /* When specified with a flush request, request that
126 /* recipients be deleted from the queue file.
127 /*
128 /* Note: the bounce daemon ignores this request when the
129 /* recipient queue file offset is <= 0.
130 /* .IP DEL_REQ_FLAG_MTA_VRFY
131 /* The message is an MTA-requested address verification probe.
132 /* Update the address verification database instead of bouncing
133 /* mail.
134 /* .IP DEL_REQ_FLAG_USR_VRFY
135 /* The message is a user-requested address expansion probe.
136 /* Update the message delivery record instead of bouncing mail.
137 /* .IP DEL_REQ_FLAG_RECORD
138 /* This is a normal message with logged delivery. Update the
139 /* message delivery record and bounce the mail.
140 /* .RE
141 /* .IP queue
142 /* The message queue name of the original message file.
143 /* .IP id
144 /* The message queue id if the original message file. The bounce log
145 /* file has the same name as the original message file.
146 /* .IP stats
147 /* Time stamps from different message delivery stages
148 /* and session reuse count.
149 /* .IP rcpt
150 /* Recipient information. See recipient_list(3).
151 /* .IP relay
152 /* Name of the host that the message could not be delivered to.
153 /* This information is used for syslogging only.
154 /* .IP encoding
155 /* The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
156 /* .IP sendopts
157 /* Sender-requested SMTPUTF8 or RequireTLS support.
158 /* .IP sender
159 /* The sender envelope address.
160 /* .IP dsn_envid
161 /* Optional DSN envelope ID.
162 /* .IP dsn_ret
163 /* Optional DSN return full/headers option.
164 /* .IP dsn
165 /* Delivery status. See dsn(3). The specified action is ignored.
166 /* .IP verp_delims
167 /* VERP delimiter characters, used when encoding the failed
168 /* sender into the envelope sender address.
169 /* DIAGNOSTICS
170 /* In case of success, these functions log the action, and return a
171 /* zero value. Otherwise, the functions return a non-zero result,
172 /* and when BOUNCE_FLAG_CLEAN is disabled, log that message
173 /* delivery is deferred.
174 /* .IP title
175 /* The origin of the optional DSN filter lookup table names.
176 /* .IP maps
177 /* The optional "type:table" DSN filter lookup table names,
178 /* separated by comma or whitespace.
179 /* BUGS
180 /* Should be replaced by routines with an attribute-value based
181 /* interface instead of an interface that uses a rigid argument list.
182 /* LICENSE
183 /* .ad
184 /* .fi
185 /* The Secure Mailer license must be distributed with this software.
186 /* AUTHOR(S)
187 /* Wietse Venema
188 /* IBM T.J. Watson Research
189 /* P.O. Box 704
190 /* Yorktown Heights, NY 10598, USA
191 /*
192 /* Wietse Venema
193 /* Google, Inc.
194 /* 111 8th Avenue
195 /* New York, NY 10011, USA
196 /*
197 /* Wietse Venema
198 /* porcupine.org
199 /*--*/
200
201 /* System library. */
202
203 #include <sys_defs.h>
204 #include <string.h>
205
206 /* Utility library. */
207
208 #include <msg.h>
209 #include <vstring.h>
210 #include <mymalloc.h>
211
212 /* Global library. */
213
214 #define DSN_INTERN
215 #include <mail_params.h>
216 #include <mail_proto.h>
217 #include <log_adhoc.h>
218 #include <dsn_util.h>
219 #include <rcpt_print.h>
220 #include <dsn_print.h>
221 #include <verify.h>
222 #include <defer.h>
223 #include <trace.h>
224 #include <bounce.h>
225
226 /* Shared internally, between bounce and defer clients. */
227
228 DSN_FILTER *delivery_status_filter;
229
230 /* bounce_append - append delivery status to per-message bounce log */
231
bounce_append(int flags,const char * id,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)232 int bounce_append(int flags, const char *id, MSG_STATS *stats,
233 RECIPIENT *rcpt, const char *relay,
234 DSN *dsn)
235 {
236 DSN my_dsn = *dsn;
237 DSN *dsn_res;
238
239 /*
240 * Sanity check. If we're really confident, change this into msg_panic
241 * (remember, this information may be under control by a hostile server).
242 */
243 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
244 msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
245 my_dsn.status = "5.0.0";
246 }
247
248 /*
249 * DSN filter (Postfix 3.0).
250 */
251 if (delivery_status_filter != 0
252 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
253 if (dsn_res->status[0] == '4')
254 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
255 my_dsn = *dsn_res;
256 }
257 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
258 }
259
260 /* bounce_append_intern - append delivery status to per-message bounce log */
261
bounce_append_intern(int flags,const char * id,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)262 int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
263 RECIPIENT *rcpt, const char *relay,
264 DSN *dsn)
265 {
266 DSN my_dsn = *dsn;
267 int status;
268
269 /*
270 * MTA-requested address verification information is stored in the verify
271 * service database.
272 */
273 if (flags & DEL_REQ_FLAG_MTA_VRFY) {
274 my_dsn.action = "undeliverable";
275 status = verify_append(id, stats, rcpt, relay, &my_dsn,
276 DEL_RCPT_STAT_BOUNCE);
277 return (status);
278 }
279
280 /*
281 * User-requested address verification information is logged and mailed
282 * to the requesting user.
283 */
284 if (flags & DEL_REQ_FLAG_USR_VRFY) {
285 my_dsn.action = "undeliverable";
286 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
287 return (status);
288 }
289
290 /*
291 * Normal (well almost) delivery. When we're pretending that we can't
292 * bounce, don't create a defer log file when we wouldn't keep the bounce
293 * log file. That's a lot of negatives in one sentence.
294 */
295 else if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN)) {
296 return (-1);
297 }
298
299 /*
300 * Normal mail delivery. May also send a delivery record to the user.
301 *
302 * XXX DSN We write all recipients to the bounce logfile regardless of DSN
303 * NOTIFY options, because those options don't apply to postmaster
304 * notifications.
305 */
306 else {
307 char *my_status = mystrdup(my_dsn.status);
308 const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
309
310 /*
311 * Supply default action.
312 */
313 my_dsn.status = my_status;
314 if (var_soft_bounce) {
315 my_status[0] = '4';
316 my_dsn.action = "delayed";
317 } else {
318 my_dsn.action = "failed";
319 }
320
321 if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
322 var_defer_service : var_bounce_service,
323 MAIL_ATTR_PROTO_BOUNCE,
324 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND),
325 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
326 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
327 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
328 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
329 ATTR_TYPE_END) == 0
330 && ((flags & DEL_REQ_FLAG_RECORD) == 0
331 || trace_append(flags, id, stats, rcpt, relay,
332 &my_dsn) == 0)) {
333 log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status);
334 status = (var_soft_bounce ? -1 : 0);
335 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
336 VSTRING *junk = vstring_alloc(100);
337
338 my_dsn.status = "4.3.0";
339 vstring_sprintf(junk, "%s or %s service failure",
340 var_bounce_service, var_trace_service);
341 my_dsn.reason = vstring_str(junk);
342 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
343 vstring_free(junk);
344 } else {
345 status = -1;
346 }
347 myfree(my_status);
348 return (status);
349 }
350 }
351
352 /* bounce_flush - flush the bounce log and deliver to the sender */
353
bounce_flush(int flags,const char * queue,const char * id,const char * encoding,int sendopts,const char * sender,const char * dsn_envid,int dsn_ret)354 int bounce_flush(int flags, const char *queue, const char *id,
355 const char *encoding, int sendopts,
356 const char *sender, const char *dsn_envid,
357 int dsn_ret)
358 {
359
360 /*
361 * When we're pretending that we can't bounce, don't send a bounce
362 * message.
363 */
364 if (var_soft_bounce)
365 return (-1);
366 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
367 MAIL_ATTR_PROTO_BOUNCE,
368 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH),
369 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
370 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
371 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
372 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
373 SEND_ATTR_INT(MAIL_ATTR_SENDOPTS, sendopts),
374 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
375 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
376 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
377 ATTR_TYPE_END) == 0) {
378 return (0);
379 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
380 msg_info("%s: status=deferred (bounce failed)", id);
381 return (-1);
382 } else {
383 return (-1);
384 }
385 }
386
387 /* bounce_flush_verp - verpified notification */
388
bounce_flush_verp(int flags,const char * queue,const char * id,const char * encoding,int sendopts,const char * sender,const char * dsn_envid,int dsn_ret,const char * verp_delims)389 int bounce_flush_verp(int flags, const char *queue, const char *id,
390 const char *encoding, int sendopts,
391 const char *sender, const char *dsn_envid,
392 int dsn_ret, const char *verp_delims)
393 {
394
395 /*
396 * When we're pretending that we can't bounce, don't send a bounce
397 * message.
398 */
399 if (var_soft_bounce)
400 return (-1);
401 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
402 MAIL_ATTR_PROTO_BOUNCE,
403 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_VERP),
404 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
405 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
406 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
407 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
408 SEND_ATTR_INT(MAIL_ATTR_SENDOPTS, sendopts),
409 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
410 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
411 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
412 SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims),
413 ATTR_TYPE_END) == 0) {
414 return (0);
415 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
416 msg_info("%s: status=deferred (bounce failed)", id);
417 return (-1);
418 } else {
419 return (-1);
420 }
421 }
422
423 /* bounce_one - send notice for one recipient */
424
bounce_one(int flags,const char * queue,const char * id,const char * encoding,int sendopts,const char * sender,const char * dsn_envid,int dsn_ret,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)425 int bounce_one(int flags, const char *queue, const char *id,
426 const char *encoding, int sendopts,
427 const char *sender, const char *dsn_envid,
428 int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt,
429 const char *relay, DSN *dsn)
430 {
431 DSN my_dsn = *dsn;
432 DSN *dsn_res;
433
434 /*
435 * Sanity check.
436 */
437 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
438 msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
439 my_dsn.status = "5.0.0";
440 }
441
442 /*
443 * DSN filter (Postfix 3.0).
444 */
445 if (delivery_status_filter != 0
446 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
447 if (dsn_res->status[0] == '4')
448 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
449 my_dsn = *dsn_res;
450 }
451 return (bounce_one_intern(flags, queue, id, encoding, sendopts, sender,
452 dsn_envid, dsn_ret, stats, rcpt, relay, &my_dsn));
453 }
454
455 /* bounce_one_intern - send notice for one recipient */
456
bounce_one_intern(int flags,const char * queue,const char * id,const char * encoding,int sendopts,const char * sender,const char * dsn_envid,int dsn_ret,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)457 int bounce_one_intern(int flags, const char *queue, const char *id,
458 const char *encoding, int sendopts,
459 const char *sender, const char *dsn_envid,
460 int dsn_ret, MSG_STATS *stats,
461 RECIPIENT *rcpt, const char *relay,
462 DSN *dsn)
463 {
464 DSN my_dsn = *dsn;
465 int status;
466
467 /*
468 * MTA-requested address verification information is stored in the verify
469 * service database.
470 */
471 if (flags & DEL_REQ_FLAG_MTA_VRFY) {
472 my_dsn.action = "undeliverable";
473 status = verify_append(id, stats, rcpt, relay, &my_dsn,
474 DEL_RCPT_STAT_BOUNCE);
475 return (status);
476 }
477
478 /*
479 * User-requested address verification information is logged and mailed
480 * to the requesting user.
481 */
482 if (flags & DEL_REQ_FLAG_USR_VRFY) {
483 my_dsn.action = "undeliverable";
484 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
485 return (status);
486 }
487
488 /*
489 * When we're not bouncing, then use the standard multi-recipient logfile
490 * based procedure.
491 */
492 else if (var_soft_bounce) {
493 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
494 }
495
496 /*
497 * Normal mail delivery. May also send a delivery record to the user.
498 *
499 * XXX DSN We send all recipients regardless of DSN NOTIFY options, because
500 * those options don't apply to postmaster notifications.
501 */
502 else {
503
504 /*
505 * Supply default action.
506 */
507 my_dsn.action = "failed";
508
509 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
510 MAIL_ATTR_PROTO_BOUNCE,
511 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_ONE),
512 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
513 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
514 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
515 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
516 SEND_ATTR_INT(MAIL_ATTR_SENDOPTS, sendopts),
517 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
518 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
519 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
520 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
521 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
522 ATTR_TYPE_END) == 0
523 && ((flags & DEL_REQ_FLAG_RECORD) == 0
524 || trace_append(flags, id, stats, rcpt, relay,
525 &my_dsn) == 0)) {
526 log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced");
527 status = 0;
528 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
529 VSTRING *junk = vstring_alloc(100);
530
531 my_dsn.status = "4.3.0";
532 vstring_sprintf(junk, "%s or %s service failure",
533 var_bounce_service, var_trace_service);
534 my_dsn.reason = vstring_str(junk);
535 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
536 vstring_free(junk);
537 } else {
538 status = -1;
539 }
540 return (status);
541 }
542 }
543
544 /* bounce_client_init - initialize bounce/defer DSN filter */
545
bounce_client_init(const char * title,const char * maps)546 void bounce_client_init(const char *title, const char *maps)
547 {
548 static const char myname[] = "bounce_client_init";
549
550 if (delivery_status_filter != 0)
551 msg_panic("%s: duplicate initialization", myname);
552 if (*maps)
553 delivery_status_filter = dsn_filter_create(title, maps);
554 }
555