1 /*	$NetBSD: bounce.c,v 1.1.1.1 2009/06/23 10:08:45 tron 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, sender,
20 /*				dsn_envid, dsn_ret)
21 /*	int	flags;
22 /*	const char *queue;
23 /*	const char *id;
24 /*	const char *encoding;
25 /*	const char *sender;
26 /*	const char *dsn_envid;
27 /*	int	dsn_ret;
28 /*
29 /*	int	bounce_flush_verp(flags, queue, id, encoding, sender,
30 /*				dsn_envid, dsn_ret, verp_delims)
31 /*	int	flags;
32 /*	const char *queue;
33 /*	const char *id;
34 /*	const char *encoding;
35 /*	const char *sender;
36 /*	const char *dsn_envid;
37 /*	int	dsn_ret;
38 /*	const char *verp_delims;
39 /*
40 /*	int	bounce_one(flags, queue, id, encoding, sender, envid, ret,
41 /*				stats, recipient, relay, dsn)
42 /*	int	flags;
43 /*	const char *queue;
44 /*	const char *id;
45 /*	const char *encoding;
46 /*	const char *sender;
47 /*	const char *dsn_envid;
48 /*	int	dsn_ret;
49 /*	MSG_STATS *stats;
50 /*	RECIPIENT *rcpt;
51 /*	const char *relay;
52 /*	DSN	*dsn;
53 /* DESCRIPTION
54 /*	This module implements the client interface to the message
55 /*	bounce service, which maintains a per-message log of status
56 /*	records with recipients that were bounced, and the dsn_text why.
57 /*
58 /*	bounce_append() appends a dsn_text for non-delivery to the
59 /*	bounce log for the named recipient, updates the address
60 /*	verification service, or updates a message delivery record
61 /*	on request by the sender. The flags argument determines
62 /*	the action.
63 /*
64 /*	bounce_flush() actually bounces the specified message to
65 /*	the specified sender, including the bounce log that was
66 /*	built with bounce_append(). The bounce logfile is removed
67 /*	upon successful completion.
68 /*
69 /*	bounce_flush_verp() is like bounce_flush(), but sends one
70 /*	notification per recipient, with the failed recipient encoded
71 /*	into the sender address.
72 /*
73 /*	bounce_one() bounces one recipient and immediately sends a
74 /*	notification to the sender. This procedure does not append
75 /*	the recipient and dsn_text to the per-message bounce log, and
76 /*	should be used when a delivery agent changes the error
77 /*	return address in a manner that depends on the recipient
78 /*	address.
79 /*
80 /*	Arguments:
81 /* .IP flags
82 /*	The bitwise OR of zero or more of the following (specify
83 /*	BOUNCE_FLAG_NONE to request no special processing):
84 /* .RS
85 /* .IP BOUNCE_FLAG_CLEAN
86 /*	Delete the bounce log in case of an error (as in: pretend
87 /*	that we never even tried to bounce this message).
88 /* .IP BOUNCE_FLAG_DELRCPT
89 /*	When specified with a flush request, request that
90 /*	recipients be deleted from the queue file.
91 /*
92 /*	Note: the bounce daemon ignores this request when the
93 /*	recipient queue file offset is <= 0.
94 /* .IP DEL_REQ_FLAG_MTA_VRFY
95 /*	The message is an MTA-requested address verification probe.
96 /*	Update the address verification database instead of bouncing
97 /*	mail.
98 /* .IP DEL_REQ_FLAG_USR_VRFY
99 /*	The message is a user-requested address expansion probe.
100 /*	Update the message delivery record instead of bouncing mail.
101 /* .IP DEL_REQ_FLAG_RECORD
102 /*	This is a normal message with logged delivery. Update the
103 /*	message delivery record and bounce the mail.
104 /* .RE
105 /* .IP queue
106 /*	The message queue name of the original message file.
107 /* .IP id
108 /*	The message queue id if the original message file. The bounce log
109 /*	file has the same name as the original message file.
110 /* .IP stats
111 /*	Time stamps from different message delivery stages
112 /*	and session reuse count.
113 /* .IP rcpt
114 /*	Recipient information. See recipient_list(3).
115 /* .IP relay
116 /*	Name of the host that the message could not be delivered to.
117 /*	This information is used for syslogging only.
118 /* .IP encoding
119 /*	The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
120 /* .IP sender
121 /*	The sender envelope address.
122 /* .IP dsn_envid
123 /*	Optional DSN envelope ID.
124 /* .IP dsn_ret
125 /*	Optional DSN return full/headers option.
126 /* .IP dsn
127 /*	Delivery status. See dsn(3). The specified action is ignored.
128 /* .IP verp_delims
129 /*	VERP delimiter characters, used when encoding the failed
130 /*	sender into the envelope sender address.
131 /* DIAGNOSTICS
132 /*	In case of success, these functions log the action, and return a
133 /*	zero value. Otherwise, the functions return a non-zero result,
134 /*	and when BOUNCE_FLAG_CLEAN is disabled, log that message
135 /*	delivery is deferred.
136 /* BUGS
137 /*	Should be replaced by routines with an attribute-value based
138 /*	interface instead of an interface that uses a rigid argument list.
139 /* LICENSE
140 /* .ad
141 /* .fi
142 /*	The Secure Mailer license must be distributed with this software.
143 /* AUTHOR(S)
144 /*	Wietse Venema
145 /*	IBM T.J. Watson Research
146 /*	P.O. Box 704
147 /*	Yorktown Heights, NY 10598, USA
148 /*--*/
149 
150 /* System library. */
151 
152 #include <sys_defs.h>
153 #include <string.h>
154 
155 /* Utility library. */
156 
157 #include <msg.h>
158 #include <vstring.h>
159 #include <mymalloc.h>
160 
161 /* Global library. */
162 
163 #include <mail_params.h>
164 #include <mail_proto.h>
165 #include <log_adhoc.h>
166 #include <dsn_util.h>
167 #include <rcpt_print.h>
168 #include <dsn_print.h>
169 #include <verify.h>
170 #include <defer.h>
171 #include <trace.h>
172 #include <bounce.h>
173 
174 /* bounce_append - append dsn_text to per-message bounce log */
175 
176 int     bounce_append(int flags, const char *id, MSG_STATS *stats,
177 		              RECIPIENT *rcpt, const char *relay,
178 		              DSN *dsn)
179 {
180     DSN     my_dsn = *dsn;
181     int     status;
182 
183     /*
184      * Sanity check. If we're really confident, change this into msg_panic
185      * (remember, this information may be under control by a hostile server).
186      */
187     if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
188 	msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
189 	my_dsn.status = "5.0.0";
190     }
191 
192     /*
193      * MTA-requested address verification information is stored in the verify
194      * service database.
195      */
196     if (flags & DEL_REQ_FLAG_MTA_VRFY) {
197 	my_dsn.action = "undeliverable";
198 	status = verify_append(id, stats, rcpt, relay, &my_dsn,
199 			       DEL_RCPT_STAT_BOUNCE);
200 	return (status);
201     }
202 
203     /*
204      * User-requested address verification information is logged and mailed
205      * to the requesting user.
206      */
207     if (flags & DEL_REQ_FLAG_USR_VRFY) {
208 	my_dsn.action = "undeliverable";
209 	status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
210 	return (status);
211     }
212 
213     /*
214      * Normal (well almost) delivery. When we're pretending that we can't
215      * bounce, don't create a defer log file when we wouldn't keep the bounce
216      * log file. That's a lot of negatives in one sentence.
217      */
218     else if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN)) {
219 	return (-1);
220     }
221 
222     /*
223      * Normal mail delivery. May also send a delivery record to the user.
224      *
225      * XXX DSN We write all recipients to the bounce logfile regardless of DSN
226      * NOTIFY options, because those options don't apply to postmaster
227      * notifications.
228      */
229     else {
230 	char   *my_status = mystrdup(my_dsn.status);
231 	const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
232 
233 	/*
234 	 * Supply default action.
235 	 */
236 	my_dsn.status = my_status;
237 	if (var_soft_bounce) {
238 	    my_status[0] = '4';
239 	    my_dsn.action = "delayed";
240 	} else {
241 	    my_dsn.action = "failed";
242 	}
243 
244 	if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
245 				var_defer_service : var_bounce_service,
246 			   ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
247 				ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
248 				ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
249 				ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
250 				ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
251 				ATTR_TYPE_END) == 0
252 	    && ((flags & DEL_REQ_FLAG_RECORD) == 0
253 		|| trace_append(flags, id, stats, rcpt, relay,
254 				&my_dsn) == 0)) {
255 	    log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status);
256 	    status = (var_soft_bounce ? -1 : 0);
257 	} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
258 	    VSTRING *junk = vstring_alloc(100);
259 
260 	    my_dsn.status = "4.3.0";
261 	    vstring_sprintf(junk, "%s or %s service failure",
262 			    var_bounce_service, var_trace_service);
263 	    my_dsn.reason = vstring_str(junk);
264 	    status = defer_append(flags, id, stats, rcpt, relay, &my_dsn);
265 	    vstring_free(junk);
266 	} else {
267 	    status = -1;
268 	}
269 	myfree(my_status);
270 	return (status);
271     }
272 }
273 
274 /* bounce_flush - flush the bounce log and deliver to the sender */
275 
276 int     bounce_flush(int flags, const char *queue, const char *id,
277 		             const char *encoding, const char *sender,
278 		             const char *dsn_envid, int dsn_ret)
279 {
280 
281     /*
282      * When we're pretending that we can't bounce, don't send a bounce
283      * message.
284      */
285     if (var_soft_bounce)
286 	return (-1);
287     if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
288 			    ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
289 			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
290 			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
291 			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
292 			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
293 			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
294 			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
295 			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
296 			    ATTR_TYPE_END) == 0) {
297 	return (0);
298     } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
299 	msg_info("%s: status=deferred (bounce failed)", id);
300 	return (-1);
301     } else {
302 	return (-1);
303     }
304 }
305 
306 /* bounce_flush_verp - verpified notification */
307 
308 int     bounce_flush_verp(int flags, const char *queue, const char *id,
309 			          const char *encoding, const char *sender,
310 			          const char *dsn_envid, int dsn_ret,
311 			          const char *verp_delims)
312 {
313 
314     /*
315      * When we're pretending that we can't bounce, don't send a bounce
316      * message.
317      */
318     if (var_soft_bounce)
319 	return (-1);
320     if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
321 			    ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_VERP,
322 			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
323 			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
324 			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
325 			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
326 			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
327 			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
328 			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
329 			    ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
330 			    ATTR_TYPE_END) == 0) {
331 	return (0);
332     } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
333 	msg_info("%s: status=deferred (bounce failed)", id);
334 	return (-1);
335     } else {
336 	return (-1);
337     }
338 }
339 
340 /* bounce_one - send notice for one recipient */
341 
342 int     bounce_one(int flags, const char *queue, const char *id,
343 		           const char *encoding, const char *sender,
344 		           const char *dsn_envid, int dsn_ret,
345 		           MSG_STATS *stats, RECIPIENT *rcpt,
346 		           const char *relay, DSN *dsn)
347 {
348     DSN     my_dsn = *dsn;
349     int     status;
350 
351     /*
352      * Sanity check.
353      */
354     if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
355 	msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
356 	my_dsn.status = "5.0.0";
357     }
358 
359     /*
360      * MTA-requested address verification information is stored in the verify
361      * service database.
362      */
363     if (flags & DEL_REQ_FLAG_MTA_VRFY) {
364 	my_dsn.action = "undeliverable";
365 	status = verify_append(id, stats, rcpt, relay, &my_dsn,
366 			       DEL_RCPT_STAT_BOUNCE);
367 	return (status);
368     }
369 
370     /*
371      * User-requested address verification information is logged and mailed
372      * to the requesting user.
373      */
374     if (flags & DEL_REQ_FLAG_USR_VRFY) {
375 	my_dsn.action = "undeliverable";
376 	status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
377 	return (status);
378     }
379 
380     /*
381      * When we're not bouncing, then use the standard multi-recipient logfile
382      * based procedure.
383      */
384     else if (var_soft_bounce) {
385 	return (bounce_append(flags, id, stats, rcpt, relay, &my_dsn));
386     }
387 
388     /*
389      * Normal mail delivery. May also send a delivery record to the user.
390      *
391      * XXX DSN We send all recipients regardless of DSN NOTIFY options, because
392      * those options don't apply to postmaster notifications.
393      */
394     else {
395 
396 	/*
397 	 * Supply default action.
398 	 */
399 	my_dsn.action = "failed";
400 
401 	if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
402 			      ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_ONE,
403 				ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
404 				ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
405 				ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
406 				ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
407 				ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
408 			      ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
409 				ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
410 				ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
411 				ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
412 				ATTR_TYPE_END) == 0
413 	    && ((flags & DEL_REQ_FLAG_RECORD) == 0
414 		|| trace_append(flags, id, stats, rcpt, relay,
415 				&my_dsn) == 0)) {
416 	    log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced");
417 	    status = 0;
418 	} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
419 	    VSTRING *junk = vstring_alloc(100);
420 
421 	    my_dsn.status = "4.3.0";
422 	    vstring_sprintf(junk, "%s or %s service failure",
423 			    var_bounce_service, var_trace_service);
424 	    my_dsn.reason = vstring_str(junk);
425 	    status = defer_append(flags, id, stats, rcpt, relay, &my_dsn);
426 	    vstring_free(junk);
427 	} else {
428 	    status = -1;
429 	}
430 	return (status);
431     }
432 }
433