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