1 /* $NetBSD: bounce_notify_verp.c,v 1.2 2017/02/14 01:16:44 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* bounce_notify_verp 3
6 /* SUMMARY
7 /* send non-delivery report to sender, server side
8 /* SYNOPSIS
9 /* #include "bounce_service.h"
10 /*
11 /* int bounce_notify_verp(flags, service, queue_name, queue_id,
12 /* encoding, smtputf8, sender,
13 /* dsn_envid, dsn_ret, verp_delims,
14 /* templates)
15 /* int flags;
16 /* char *service;
17 /* char *queue_name;
18 /* char *queue_id;
19 /* char *encoding;
20 /* int smtputf8;
21 /* char *sender;
22 /* char *dsn_envid;
23 /* int dsn_ret;
24 /* char *verp_delims;
25 /* BOUNCE_TEMPLATES *templates;
26 /* DESCRIPTION
27 /* This module implements the server side of the bounce_notify()
28 /* (send bounce message) request. The logfile
29 /* is removed after and a warning is posted.
30 /* The bounce recipient address is encoded in VERP format.
31 /* This routine must be used for single bounces only.
32 /*
33 /* When a message bounces, a full copy is sent to the originator,
34 /* and an optional copy of the diagnostics with message headers is
35 /* sent to the postmaster. The result is non-zero when the operation
36 /* should be tried again.
37 /*
38 /* When a bounce is sent, the sender address is the empty
39 /* address.
40 /* DIAGNOSTICS
41 /* Fatal error: error opening existing file.
42 /* SEE ALSO
43 /* bounce(3) basic bounce service client interface
44 /* LICENSE
45 /* .ad
46 /* .fi
47 /* The Secure Mailer license must be distributed with this software.
48 /* AUTHOR(S)
49 /* Wietse Venema
50 /* IBM T.J. Watson Research
51 /* P.O. Box 704
52 /* Yorktown Heights, NY 10598, USA
53 /*--*/
54
55 /* System library. */
56
57 #include <sys_defs.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <ctype.h>
62
63 /* Utility library. */
64
65 #include <msg.h>
66 #include <vstream.h>
67 #include <name_mask.h>
68 #include <stringops.h>
69
70 /* Global library. */
71
72 #include <mail_params.h>
73 #include <mail_queue.h>
74 #include <post_mail.h>
75 #include <mail_addr.h>
76 #include <mail_error.h>
77 #include <verp_sender.h>
78 #include <bounce.h>
79 #include <dsn_mask.h>
80 #include <rec_type.h>
81
82 /* Application-specific. */
83
84 #include "bounce_service.h"
85
86 #define STR vstring_str
87
88 /* bounce_notify_verp - send a bounce, VERP style */
89
bounce_notify_verp(int flags,char * service,char * queue_name,char * queue_id,char * encoding,int smtputf8,char * recipient,char * dsn_envid,int dsn_ret,char * verp_delims,BOUNCE_TEMPLATES * ts)90 int bounce_notify_verp(int flags, char *service, char *queue_name,
91 char *queue_id, char *encoding,
92 int smtputf8, char *recipient,
93 char *dsn_envid, int dsn_ret,
94 char *verp_delims, BOUNCE_TEMPLATES *ts)
95 {
96 const char *myname = "bounce_notify_verp";
97 BOUNCE_INFO *bounce_info;
98 int bounce_status = 0;
99 int postmaster_status;
100 VSTREAM *bounce;
101 int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
102 var_notify_classes);
103 char *postmaster;
104 VSTRING *verp_buf;
105 VSTRING *new_id;
106
107 /*
108 * Sanity checks. We must be called only for undeliverable non-bounce
109 * messages.
110 */
111 if (*recipient == 0)
112 msg_panic("%s: attempt to bounce a single bounce", myname);
113 if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0)
114 msg_panic("%s: attempt to bounce a double bounce", myname);
115
116 /*
117 * Initialize. Open queue file, bounce log, etc.
118 */
119 bounce_info = bounce_mail_init(service, queue_name, queue_id,
120 encoding, smtputf8, dsn_envid,
121 ts->failure);
122
123 /*
124 * If we have no recipient list then we can't send VERP replies. Send
125 * *something* anyway so that the mail is not lost in a black hole.
126 */
127 if (bounce_info->log_handle == 0) {
128 DSN_BUF *dsn_buf = dsb_create();
129 RCPT_BUF *rcpt_buf = rcpb_create();
130
131 dsb_simple(dsn_buf, "5.0.0", "(error report unavailable)");
132 (void) DSN_FROM_DSN_BUF(dsn_buf);
133 vstring_strcpy(rcpt_buf->address, "(recipient address unavailable)");
134 (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
135 bounce_status = bounce_one_service(flags, queue_name, queue_id,
136 encoding, smtputf8, recipient,
137 dsn_envid, dsn_ret, rcpt_buf,
138 dsn_buf, ts);
139 rcpb_free(rcpt_buf);
140 dsb_free(dsn_buf);
141 bounce_mail_free(bounce_info);
142 return (bounce_status);
143 }
144 #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */
145 #define NULL_TRACE_FLAGS 0
146
147 /*
148 * A non-bounce message was returned. Send a single bounce, one per
149 * recipient.
150 */
151 verp_buf = vstring_alloc(100);
152 new_id = vstring_alloc(10);
153 while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
154 bounce_info->dsn_buf) != 0) {
155 RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
156
157 /*
158 * Notify the originator, subject to DSN NOTIFY restrictions.
159 *
160 * Fix 20090114: Use the Postfix original recipient, because that is
161 * what the VERP consumer expects.
162 */
163 if (rcpt->dsn_notify != 0 /* compat */
164 && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
165 bounce_status = 0;
166 } else {
167 verp_sender(verp_buf, verp_delims, recipient, rcpt);
168 if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
169 MAIL_SRC_MASK_BOUNCE,
170 NULL_TRACE_FLAGS,
171 smtputf8,
172 new_id)) != 0) {
173
174 /*
175 * Send the bounce message header, some boilerplate text that
176 * pretends that we are a polite mail system, the text with
177 * reason for the bounce, and a copy of the original message.
178 */
179 if (bounce_header(bounce, bounce_info, STR(verp_buf),
180 NO_POSTMASTER_COPY) == 0
181 && bounce_boilerplate(bounce, bounce_info) == 0
182 && bounce_recipient_log(bounce, bounce_info) == 0
183 && bounce_header_dsn(bounce, bounce_info) == 0
184 && bounce_recipient_dsn(bounce, bounce_info) == 0)
185 bounce_original(bounce, bounce_info, dsn_ret ?
186 dsn_ret : DSN_RET_FULL);
187 bounce_status = post_mail_fclose(bounce);
188 if (bounce_status == 0)
189 msg_info("%s: sender non-delivery notification: %s",
190 queue_id, STR(new_id));
191 } else
192 bounce_status = 1;
193
194 /*
195 * Stop at the first sign of trouble, instead of making the
196 * problem worse.
197 */
198 if (bounce_status != 0)
199 break;
200
201 /*
202 * Optionally, mark this recipient as done.
203 */
204 if (flags & BOUNCE_FLAG_DELRCPT)
205 bounce_delrcpt_one(bounce_info);
206 }
207
208 /*
209 * Optionally, send a postmaster notice, subject to notify_classes
210 * restrictions.
211 *
212 * This postmaster notice is not critical, so if it fails don't
213 * retransmit the bounce that we just generated, just log a warning.
214 */
215 #define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
216
217 if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) {
218
219 /*
220 * Send the text with reason for the bounce, and the headers of
221 * the original message. Don't bother sending the boiler-plate
222 * text. This postmaster notice is not critical, so if it fails
223 * don't retransmit the bounce that we just generated, just log a
224 * warning.
225 */
226 postmaster = var_bounce_rcpt;
227 if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
228 postmaster,
229 MAIL_SRC_MASK_BOUNCE,
230 NULL_TRACE_FLAGS,
231 smtputf8,
232 new_id)) != 0) {
233 if (bounce_header(bounce, bounce_info, postmaster,
234 POSTMASTER_COPY) == 0
235 && bounce_recipient_log(bounce, bounce_info) == 0
236 && bounce_header_dsn(bounce, bounce_info) == 0
237 && bounce_recipient_dsn(bounce, bounce_info) == 0)
238 bounce_original(bounce, bounce_info, DSN_RET_HDRS);
239 postmaster_status = post_mail_fclose(bounce);
240 if (postmaster_status == 0)
241 msg_info("%s: postmaster non-delivery notification: %s",
242 queue_id, STR(new_id));
243 } else
244 postmaster_status = 1;
245
246 if (postmaster_status)
247 msg_warn("%s: postmaster notice failed while bouncing to %s",
248 queue_id, recipient);
249 }
250 }
251
252 /*
253 * Examine the completion status. Delete the bounce log file only when
254 * the bounce was posted successfully, and only if we are bouncing for
255 * real, not just warning.
256 */
257 if (bounce_status == 0 && mail_queue_remove(service, queue_id)
258 && errno != ENOENT)
259 msg_fatal("remove %s %s: %m", service, queue_id);
260
261 /*
262 * Cleanup.
263 */
264 bounce_mail_free(bounce_info);
265 vstring_free(verp_buf);
266 vstring_free(new_id);
267
268 return (bounce_status);
269 }
270