1 /* $NetBSD: bounce.c,v 1.3 2022/10/08 16:12: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, smtputf8, sender,
20 /* dsn_envid, dsn_ret)
21 /* int flags;
22 /* const char *queue;
23 /* const char *id;
24 /* const char *encoding;
25 /* int smtputf8;
26 /* const char *sender;
27 /* const char *dsn_envid;
28 /* int dsn_ret;
29 /*
30 /* int bounce_flush_verp(flags, queue, id, encoding, smtputf8,
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 smtputf8;
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, smtputf8, 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 smtputf8;
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, smtputf8, 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 smtputf8;
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 smtputf8
157 /* The level of SMTPUTF8 support (to be defined).
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
198 /* System library. */
199
200 #include <sys_defs.h>
201 #include <string.h>
202
203 /* Utility library. */
204
205 #include <msg.h>
206 #include <vstring.h>
207 #include <mymalloc.h>
208
209 /* Global library. */
210
211 #define DSN_INTERN
212 #include <mail_params.h>
213 #include <mail_proto.h>
214 #include <log_adhoc.h>
215 #include <dsn_util.h>
216 #include <rcpt_print.h>
217 #include <dsn_print.h>
218 #include <verify.h>
219 #include <defer.h>
220 #include <trace.h>
221 #include <bounce.h>
222
223 /* Shared internally, between bounce and defer clients. */
224
225 DSN_FILTER *delivery_status_filter;
226
227 /* bounce_append - append delivery status to per-message bounce log */
228
bounce_append(int flags,const char * id,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)229 int bounce_append(int flags, const char *id, MSG_STATS *stats,
230 RECIPIENT *rcpt, const char *relay,
231 DSN *dsn)
232 {
233 DSN my_dsn = *dsn;
234 DSN *dsn_res;
235
236 /*
237 * Sanity check. If we're really confident, change this into msg_panic
238 * (remember, this information may be under control by a hostile server).
239 */
240 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
241 msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
242 my_dsn.status = "5.0.0";
243 }
244
245 /*
246 * DSN filter (Postfix 3.0).
247 */
248 if (delivery_status_filter != 0
249 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
250 if (dsn_res->status[0] == '4')
251 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
252 my_dsn = *dsn_res;
253 }
254 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
255 }
256
257 /* bounce_append_intern - append delivery status to per-message bounce log */
258
bounce_append_intern(int flags,const char * id,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)259 int bounce_append_intern(int flags, const char *id, MSG_STATS *stats,
260 RECIPIENT *rcpt, const char *relay,
261 DSN *dsn)
262 {
263 DSN my_dsn = *dsn;
264 int status;
265
266 /*
267 * MTA-requested address verification information is stored in the verify
268 * service database.
269 */
270 if (flags & DEL_REQ_FLAG_MTA_VRFY) {
271 my_dsn.action = "undeliverable";
272 status = verify_append(id, stats, rcpt, relay, &my_dsn,
273 DEL_RCPT_STAT_BOUNCE);
274 return (status);
275 }
276
277 /*
278 * User-requested address verification information is logged and mailed
279 * to the requesting user.
280 */
281 if (flags & DEL_REQ_FLAG_USR_VRFY) {
282 my_dsn.action = "undeliverable";
283 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
284 return (status);
285 }
286
287 /*
288 * Normal (well almost) delivery. When we're pretending that we can't
289 * bounce, don't create a defer log file when we wouldn't keep the bounce
290 * log file. That's a lot of negatives in one sentence.
291 */
292 else if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN)) {
293 return (-1);
294 }
295
296 /*
297 * Normal mail delivery. May also send a delivery record to the user.
298 *
299 * XXX DSN We write all recipients to the bounce logfile regardless of DSN
300 * NOTIFY options, because those options don't apply to postmaster
301 * notifications.
302 */
303 else {
304 char *my_status = mystrdup(my_dsn.status);
305 const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
306
307 /*
308 * Supply default action.
309 */
310 my_dsn.status = my_status;
311 if (var_soft_bounce) {
312 my_status[0] = '4';
313 my_dsn.action = "delayed";
314 } else {
315 my_dsn.action = "failed";
316 }
317
318 if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
319 var_defer_service : var_bounce_service,
320 MAIL_ATTR_PROTO_BOUNCE,
321 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND),
322 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
323 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
324 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
325 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
326 ATTR_TYPE_END) == 0
327 && ((flags & DEL_REQ_FLAG_RECORD) == 0
328 || trace_append(flags, id, stats, rcpt, relay,
329 &my_dsn) == 0)) {
330 log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status);
331 status = (var_soft_bounce ? -1 : 0);
332 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
333 VSTRING *junk = vstring_alloc(100);
334
335 my_dsn.status = "4.3.0";
336 vstring_sprintf(junk, "%s or %s service failure",
337 var_bounce_service, var_trace_service);
338 my_dsn.reason = vstring_str(junk);
339 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
340 vstring_free(junk);
341 } else {
342 status = -1;
343 }
344 myfree(my_status);
345 return (status);
346 }
347 }
348
349 /* bounce_flush - flush the bounce log and deliver to the sender */
350
bounce_flush(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret)351 int bounce_flush(int flags, const char *queue, const char *id,
352 const char *encoding, int smtputf8,
353 const char *sender, const char *dsn_envid,
354 int dsn_ret)
355 {
356
357 /*
358 * When we're pretending that we can't bounce, don't send a bounce
359 * message.
360 */
361 if (var_soft_bounce)
362 return (-1);
363 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
364 MAIL_ATTR_PROTO_BOUNCE,
365 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH),
366 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
367 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
368 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
369 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
370 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
371 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
372 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
373 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
374 ATTR_TYPE_END) == 0) {
375 return (0);
376 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
377 msg_info("%s: status=deferred (bounce failed)", id);
378 return (-1);
379 } else {
380 return (-1);
381 }
382 }
383
384 /* bounce_flush_verp - verpified notification */
385
bounce_flush_verp(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,const char * verp_delims)386 int bounce_flush_verp(int flags, const char *queue, const char *id,
387 const char *encoding, int smtputf8,
388 const char *sender, const char *dsn_envid,
389 int dsn_ret, const char *verp_delims)
390 {
391
392 /*
393 * When we're pretending that we can't bounce, don't send a bounce
394 * message.
395 */
396 if (var_soft_bounce)
397 return (-1);
398 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
399 MAIL_ATTR_PROTO_BOUNCE,
400 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_VERP),
401 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
402 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
403 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
404 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
405 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
406 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
407 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
408 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
409 SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims),
410 ATTR_TYPE_END) == 0) {
411 return (0);
412 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
413 msg_info("%s: status=deferred (bounce failed)", id);
414 return (-1);
415 } else {
416 return (-1);
417 }
418 }
419
420 /* bounce_one - send notice for one recipient */
421
bounce_one(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)422 int bounce_one(int flags, const char *queue, const char *id,
423 const char *encoding, int smtputf8,
424 const char *sender, const char *dsn_envid,
425 int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt,
426 const char *relay, DSN *dsn)
427 {
428 DSN my_dsn = *dsn;
429 DSN *dsn_res;
430
431 /*
432 * Sanity check.
433 */
434 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
435 msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
436 my_dsn.status = "5.0.0";
437 }
438
439 /*
440 * DSN filter (Postfix 3.0).
441 */
442 if (delivery_status_filter != 0
443 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
444 if (dsn_res->status[0] == '4')
445 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
446 my_dsn = *dsn_res;
447 }
448 return (bounce_one_intern(flags, queue, id, encoding, smtputf8, sender,
449 dsn_envid, dsn_ret, stats, rcpt, relay, &my_dsn));
450 }
451
452 /* bounce_one_intern - send notice for one recipient */
453
bounce_one_intern(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,MSG_STATS * stats,RECIPIENT * rcpt,const char * relay,DSN * dsn)454 int bounce_one_intern(int flags, const char *queue, const char *id,
455 const char *encoding, int smtputf8,
456 const char *sender, const char *dsn_envid,
457 int dsn_ret, MSG_STATS *stats,
458 RECIPIENT *rcpt, const char *relay,
459 DSN *dsn)
460 {
461 DSN my_dsn = *dsn;
462 int status;
463
464 /*
465 * MTA-requested address verification information is stored in the verify
466 * service database.
467 */
468 if (flags & DEL_REQ_FLAG_MTA_VRFY) {
469 my_dsn.action = "undeliverable";
470 status = verify_append(id, stats, rcpt, relay, &my_dsn,
471 DEL_RCPT_STAT_BOUNCE);
472 return (status);
473 }
474
475 /*
476 * User-requested address verification information is logged and mailed
477 * to the requesting user.
478 */
479 if (flags & DEL_REQ_FLAG_USR_VRFY) {
480 my_dsn.action = "undeliverable";
481 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
482 return (status);
483 }
484
485 /*
486 * When we're not bouncing, then use the standard multi-recipient logfile
487 * based procedure.
488 */
489 else if (var_soft_bounce) {
490 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
491 }
492
493 /*
494 * Normal mail delivery. May also send a delivery record to the user.
495 *
496 * XXX DSN We send all recipients regardless of DSN NOTIFY options, because
497 * those options don't apply to postmaster notifications.
498 */
499 else {
500
501 /*
502 * Supply default action.
503 */
504 my_dsn.action = "failed";
505
506 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
507 MAIL_ATTR_PROTO_BOUNCE,
508 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_ONE),
509 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
510 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
511 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
512 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
513 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
514 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
515 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
516 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
517 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
518 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn),
519 ATTR_TYPE_END) == 0
520 && ((flags & DEL_REQ_FLAG_RECORD) == 0
521 || trace_append(flags, id, stats, rcpt, relay,
522 &my_dsn) == 0)) {
523 log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced");
524 status = 0;
525 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
526 VSTRING *junk = vstring_alloc(100);
527
528 my_dsn.status = "4.3.0";
529 vstring_sprintf(junk, "%s or %s service failure",
530 var_bounce_service, var_trace_service);
531 my_dsn.reason = vstring_str(junk);
532 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn);
533 vstring_free(junk);
534 } else {
535 status = -1;
536 }
537 return (status);
538 }
539 }
540
541 /* bounce_client_init - initialize bounce/defer DSN filter */
542
bounce_client_init(const char * title,const char * maps)543 void bounce_client_init(const char *title, const char *maps)
544 {
545 static const char myname[] = "bounce_client_init";
546
547 if (delivery_status_filter != 0)
548 msg_panic("%s: duplicate initialization", myname);
549 if (*maps)
550 delivery_status_filter = dsn_filter_create(title, maps);
551 }
552