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