xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/abounce.c (revision 796c32c94f6e154afc9de0f63da35c91bb739b45)
1 /*	$NetBSD: abounce.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	abounce 3
6 /* SUMMARY
7 /*	asynchronous bounce/defer/trace service client
8 /* SYNOPSIS
9 /*	#include <abounce.h>
10 /*
11 /*	void	abounce_flush(flags, queue, id, encoding, smtputf8, sender,
12 /*				dsn_envid, dsn_ret, callback, context)
13 /*	int	flags;
14 /*	const char *queue;
15 /*	const char *id;
16 /*	const char *encoding;
17 /*	int	smtputf8;
18 /*	const char *sender;
19 /*	const char *dsn_envid;
20 /*	int	dsn_ret;
21 /*	void	(*callback)(int status, void *context);
22 /*	void	*context;
23 /*
24 /*	void	abounce_flush_verp(flags, queue, id, encoding, smtputf8, sender,
25 /*				dsn_envid, dsn_ret, verp, callback, context)
26 /*	int	flags;
27 /*	const char *queue;
28 /*	const char *id;
29 /*	const char *encoding;
30 /*	int	smtputf8;
31 /*	const char *sender;
32 /*	const char *dsn_envid;
33 /*	int	dsn_ret;
34 /*	const char *verp;
35 /*	void	(*callback)(int status, void *context);
36 /*	void	*context;
37 /*
38 /*	void	adefer_flush(flags, queue, id, encoding, smtputf8, sender,
39 /*				dsn_envid, dsn_ret, callback, context)
40 /*	int	flags;
41 /*	const char *queue;
42 /*	const char *id;
43 /*	const char *encoding;
44 /*	int	smtputf8;
45 /*	const char *sender;
46 /*	const char *dsn_envid;
47 /*	int	dsn_ret;
48 /*	void	(*callback)(int status, void *context);
49 /*	void	*context;
50 /*
51 /*	void	adefer_flush_verp(flags, queue, id, encoding, smtputf8, sender,
52 /*				dsn_envid, dsn_ret, verp, callback, context)
53 /*	int	flags;
54 /*	const char *queue;
55 /*	const char *id;
56 /*	const char *encoding;
57 /*	int	smtputf8;
58 /*	const char *sender;
59 /*	const char *dsn_envid;
60 /*	int	dsn_ret;
61 /*	const char *verp;
62 /*	void	(*callback)(int status, void *context);
63 /*	void	*context;
64 /*
65 /*	void	adefer_warn(flags, queue, id, encoding, smtputf8, sender,
66 /*				dsn_envid, dsn_ret, callback, context)
67 /*	int	flags;
68 /*	const char *queue;
69 /*	const char *id;
70 /*	const char *encoding;
71 /*	int	smtputf8;
72 /*	const char *sender;
73 /*	const char *dsn_envid;
74 /*	int	dsn_ret;
75 /*	void	(*callback)(int status, void *context);
76 /*	void	*context;
77 /*
78 /*	void	atrace_flush(flags, queue, id, encoding, smtputf8, sender,
79 /*				dsn_envid, dsn_ret, callback, context)
80 /*	int	flags;
81 /*	const char *queue;
82 /*	const char *id;
83 /*	const char *encoding;
84 /*	int	smtputf8;
85 /*	const char *sender;
86 /*	const char *dsn_envid;
87 /*	int	dsn_ret;
88 /*	void	(*callback)(int status, void *context);
89 /*	void	*context;
90 /* DESCRIPTION
91 /*	This module implements an asynchronous interface to the
92 /*	bounce/defer/trace service for submitting sender notifications
93 /*	without waiting for completion of the request.
94 /*
95 /*	abounce_flush() bounces the specified message to
96 /*	the specified sender, including the bounce log that was
97 /*	built with bounce_append().
98 /*
99 /*	abounce_flush_verp() is like abounce_flush() but sends
100 /*	one VERP style notification per undeliverable recipient.
101 /*
102 /*	adefer_flush() bounces the specified message to
103 /*	the specified sender, including the defer log that was
104 /*	built with defer_append().
105 /*	adefer_flush() requests that the deferred recipients are deleted
106 /*	from the original queue file.
107 /*
108 /*	adefer_flush_verp() is like adefer_flush() but sends
109 /*	one VERP style notification per undeliverable recipient.
110 /*
111 /*	adefer_warn() sends a "mail is delayed" notification to
112 /*	the specified sender, including the defer log that was
113 /*	built with defer_append().
114 /*
115 /*	atrace_flush() returns the specified message to the specified
116 /*	sender, including the message delivery record log that was
117 /*	built with vtrace_append().
118 /*
119 /*	Arguments:
120 /* .IP flags
121 /*	The bitwise OR of zero or more of the following (specify
122 /*	BOUNCE_FLAG_NONE to request no special processing):
123 /* .RS
124 /* .IP BOUNCE_FLAG_CLEAN
125 /*	Delete the bounce log in case of an error (as in: pretend
126 /*	that we never even tried to bounce this message).
127 /* .IP BOUNCE_FLAG_DELRCPT
128 /*	When specified with a flush operation, request that
129 /*	recipients be deleted from the queue file.
130 /*
131 /*	Note: the bounce daemon ignores this request when the
132 /*	recipient queue file offset is <= 0.
133 /* .IP BOUNCE_FLAG_COPY
134 /*	Request that a postmaster copy is sent.
135 /* .RE
136 /* .IP queue
137 /*	The message queue name of the original message file.
138 /* .IP id
139 /*	The message queue id if the original message file. The bounce log
140 /*	file has the same name as the original message file.
141 /* .IP encoding
142 /*	The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
143 /* .IP smtputf8
144 /*	The level of SMTPUTF8 support (to be defined).
145 /* .IP sender
146 /*	The sender envelope address.
147 /* .IP dsn_envid
148 /*	Optional DSN envelope ID.
149 /* .IP ret
150 /*	Optional DSN return full/headers option.
151 /* .IP verp
152 /*	VERP delimiter characters.
153 /* .IP callback
154 /*	Name of a routine that receives the notification status as
155 /*	documented for bounce_flush() or defer_flush().
156 /* .IP context
157 /*	Application-specific context that is passed through to the
158 /*	callback routine. Use proper casts or the world will come
159 /*	to an end.
160 /* DIAGNOSTICS
161 /*	In case of success, these functions log the action, and return a
162 /*	zero result via the callback routine. Otherwise, the functions
163 /*	return a non-zero result via the callback routine, and when
164 /*	BOUNCE_FLAG_CLEAN is disabled, log that message delivery is deferred.
165 /* LICENSE
166 /* .ad
167 /* .fi
168 /*	The Secure Mailer license must be distributed with this software.
169 /* AUTHOR(S)
170 /*	Wietse Venema
171 /*	IBM T.J. Watson Research
172 /*	P.O. Box 704
173 /*	Yorktown Heights, NY 10598, USA
174 /*--*/
175 
176 /* System library. */
177 
178 #include <sys_defs.h>
179 
180 /* Utility library. */
181 
182 #include <msg.h>
183 #include <mymalloc.h>
184 #include <events.h>
185 #include <vstream.h>
186 
187 /* Global library. */
188 
189 #include <mail_params.h>
190 #include <mail_proto.h>
191 #include <abounce.h>
192 
193 /* Application-specific. */
194 
195  /*
196   * Each bounce/defer flush/warn request is implemented by sending the
197   * request to the bounce/defer server, and by creating a pseudo thread that
198   * suspends itself until the server replies (or dies). Upon wakeup, the
199   * pseudo thread delivers the request completion status to the application
200   * and destroys itself. The structure below maintains all the necessary
201   * request state while the pseudo thread is suspended.
202   */
203 typedef struct {
204     int     command;			/* bounce request type */
205     int     flags;			/* bounce options */
206     char   *id;				/* queue ID for logging */
207     ABOUNCE_FN callback;		/* application callback */
208     void   *context;			/* application context */
209     VSTREAM *fp;			/* server I/O handle */
210 } ABOUNCE;
211 
212  /*
213   * Encapsulate common code.
214   */
215 #define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
216 	event_enable_read((fd), (callback), (context)); \
217 	event_request_timer((callback), (context), (timeout)); \
218     } while (0)
219 
220 #define ABOUNCE_EVENT_DISABLE(fd, callback, context) do { \
221 	event_cancel_timer((callback), (context)); \
222 	event_disable_readwrite(fd); \
223     } while (0)
224 
225  /*
226   * If we set the reply timeout too short, then we make the problem worse by
227   * increasing overload. With 1000s timeout mail will keep flowing, but there
228   * will be a large number of blocked bounce processes, and some resource is
229   * likely to run out.
230   */
231 #define ABOUNCE_TIMEOUT	1000
232 
233 /* abounce_done - deliver status to application and clean up pseudo thread */
234 
235 static void abounce_done(ABOUNCE *ap, int status)
236 {
237     (void) vstream_fclose(ap->fp);
238     if (status != 0 && (ap->flags & BOUNCE_FLAG_CLEAN) == 0)
239 	msg_info("%s: status=deferred (%s failed)", ap->id,
240 		 ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
241 		 ap->command == BOUNCE_CMD_WARN ? "delay warning" :
242 		 ap->command == BOUNCE_CMD_VERP ? "verp" :
243 		 ap->command == BOUNCE_CMD_TRACE ? "trace" :
244 		 "whatever");
245     ap->callback(status, ap->context);
246     myfree(ap->id);
247     myfree((void *) ap);
248 }
249 
250 /* abounce_event - resume pseudo thread after server reply event */
251 
252 static void abounce_event(int event, void *context)
253 {
254     ABOUNCE *ap = (ABOUNCE *) context;
255     int     status;
256 
257     ABOUNCE_EVENT_DISABLE(vstream_fileno(ap->fp), abounce_event, context);
258     abounce_done(ap, (event != EVENT_TIME
259 		      && attr_scan(ap->fp, ATTR_FLAG_STRICT,
260 				   RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
261 				   ATTR_TYPE_END) == 1) ? status : -1);
262 }
263 
264 /* abounce_request_verp - suspend pseudo thread until server reply event */
265 
266 static void abounce_request_verp(const char *class, const char *service,
267 				         int command, int flags,
268 				         const char *queue, const char *id,
269 				         const char *encoding,
270 				         int smtputf8,
271 				         const char *sender,
272 				         const char *dsn_envid,
273 				         int dsn_ret,
274 				         const char *verp,
275 				         ABOUNCE_FN callback,
276 				         void *context)
277 {
278     ABOUNCE *ap;
279 
280     /*
281      * Save pseudo thread state. Connect to the server. Send the request and
282      * suspend the pseudo thread until the server replies (or dies).
283      */
284     ap = (ABOUNCE *) mymalloc(sizeof(*ap));
285     ap->command = command;
286     ap->flags = flags;
287     ap->id = mystrdup(id);
288     ap->callback = callback;
289     ap->context = context;
290     ap->fp = mail_connect_wait(class, service);
291 
292     if (attr_print(ap->fp, ATTR_FLAG_NONE,
293 		   SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
294 		   SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
295 		   SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
296 		   SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
297 		   SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
298 		   SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
299 		   SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
300 		   SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
301 		   SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
302 		   SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp),
303 		   ATTR_TYPE_END) == 0
304 	&& vstream_fflush(ap->fp) == 0) {
305 	ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
306 			     (void *) ap, ABOUNCE_TIMEOUT);
307     } else {
308 	abounce_done(ap, -1);
309     }
310 }
311 
312 /* abounce_flush_verp - asynchronous bounce flush */
313 
314 void    abounce_flush_verp(int flags, const char *queue, const char *id,
315 			           const char *encoding, int smtputf8,
316 			           const char *sender, const char *dsn_envid,
317 			           int dsn_ret, const char *verp,
318 			           ABOUNCE_FN callback,
319 			           void *context)
320 {
321     abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service,
322 		      BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
323 		       sender, dsn_envid, dsn_ret, verp, callback, context);
324 }
325 
326 /* adefer_flush_verp - asynchronous defer flush */
327 
328 void    adefer_flush_verp(int flags, const char *queue, const char *id,
329 			          const char *encoding, int smtputf8,
330 			          const char *sender, const char *dsn_envid,
331 			          int dsn_ret, const char *verp,
332 			          ABOUNCE_FN callback, void *context)
333 {
334     flags |= BOUNCE_FLAG_DELRCPT;
335     abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service,
336 		      BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
337 		       sender, dsn_envid, dsn_ret, verp, callback, context);
338 }
339 
340 /* abounce_request - suspend pseudo thread until server reply event */
341 
342 static void abounce_request(const char *class, const char *service,
343 			            int command, int flags,
344 			            const char *queue, const char *id,
345 			            const char *encoding, int smtputf8,
346 			            const char *sender,
347 			            const char *dsn_envid, int dsn_ret,
348 			            ABOUNCE_FN callback, void *context)
349 {
350     ABOUNCE *ap;
351 
352     /*
353      * Save pseudo thread state. Connect to the server. Send the request and
354      * suspend the pseudo thread until the server replies (or dies).
355      */
356     ap = (ABOUNCE *) mymalloc(sizeof(*ap));
357     ap->command = command;
358     ap->flags = flags;
359     ap->id = mystrdup(id);
360     ap->callback = callback;
361     ap->context = context;
362     ap->fp = mail_connect_wait(class, service);
363 
364     if (attr_print(ap->fp, ATTR_FLAG_NONE,
365 		   SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
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 	&& vstream_fflush(ap->fp) == 0) {
376 	ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
377 			     (void *) ap, ABOUNCE_TIMEOUT);
378     } else {
379 	abounce_done(ap, -1);
380     }
381 }
382 
383 /* abounce_flush - asynchronous bounce flush */
384 
385 void    abounce_flush(int flags, const char *queue, const char *id,
386 		              const char *encoding, int smtputf8,
387 		              const char *sender, const char *dsn_envid,
388 		              int dsn_ret, ABOUNCE_FN callback,
389 		              void *context)
390 {
391     abounce_request(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
392 		    flags, queue, id, encoding, smtputf8, sender, dsn_envid,
393 		    dsn_ret, callback, context);
394 }
395 
396 /* adefer_flush - asynchronous defer flush */
397 
398 void    adefer_flush(int flags, const char *queue, const char *id,
399 		             const char *encoding, int smtputf8,
400 		             const char *sender, const char *dsn_envid,
401 		             int dsn_ret, ABOUNCE_FN callback, void *context)
402 {
403     flags |= BOUNCE_FLAG_DELRCPT;
404     abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
405 		    flags, queue, id, encoding, smtputf8, sender, dsn_envid,
406 		    dsn_ret, callback, context);
407 }
408 
409 /* adefer_warn - send copy of defer log to sender as warning bounce */
410 
411 void    adefer_warn(int flags, const char *queue, const char *id,
412 		            const char *encoding, int smtputf8,
413 		            const char *sender, const char *dsn_envid,
414 		            int dsn_ret, ABOUNCE_FN callback, void *context)
415 {
416     abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
417 		    flags, queue, id, encoding, smtputf8, sender, dsn_envid,
418 		    dsn_ret, callback, context);
419 }
420 
421 /* atrace_flush - asynchronous trace flush */
422 
423 void    atrace_flush(int flags, const char *queue, const char *id,
424 		             const char *encoding, int smtputf8,
425 		             const char *sender, const char *dsn_envid,
426 		             int dsn_ret, ABOUNCE_FN callback, void *context)
427 {
428     abounce_request(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
429 		    flags, queue, id, encoding, smtputf8, sender, dsn_envid,
430 		    dsn_ret, callback, context);
431 }
432