1 /* $NetBSD: abounce.c,v 1.3 2022/10/08 16:12: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 /* Wietse Venema
176 /* Google, Inc.
177 /* 111 8th Avenue
178 /* New York, NY 10011, USA
179 /*--*/
180
181 /* System library. */
182
183 #include <sys_defs.h>
184
185 /* Utility library. */
186
187 #include <msg.h>
188 #include <mymalloc.h>
189 #include <events.h>
190 #include <vstream.h>
191
192 /* Global library. */
193
194 #include <mail_params.h>
195 #include <mail_proto.h>
196 #include <abounce.h>
197
198 /* Application-specific. */
199
200 /*
201 * Each bounce/defer flush/warn request is implemented by sending the
202 * request to the bounce/defer server, and by creating a pseudo thread that
203 * suspends itself until the server replies (or dies). Upon wakeup, the
204 * pseudo thread delivers the request completion status to the application
205 * and destroys itself. The structure below maintains all the necessary
206 * request state while the pseudo thread is suspended.
207 */
208 typedef struct {
209 int command; /* bounce request type */
210 int flags; /* bounce options */
211 char *id; /* queue ID for logging */
212 VSTRING *request; /* serialized request */
213 ABOUNCE_FN callback; /* application callback */
214 void *context; /* application context */
215 VSTREAM *fp; /* server I/O handle */
216 } ABOUNCE_STATE;
217
218 /*
219 * Encapsulate common code.
220 */
221 #define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
222 event_enable_read((fd), (callback), (context)); \
223 event_request_timer((callback), (context), (timeout)); \
224 } while (0)
225
226 /*
227 * If we set the reply timeout too short, then we make the problem worse by
228 * increasing overload. With 1000s timeout mail will keep flowing, but there
229 * will be a large number of blocked bounce processes, and some resource is
230 * likely to run out.
231 */
232 #define ABOUNCE_TIMEOUT 1000
233
234 /*
235 * The initial buffer size for a serialized request.
236 */
237 #define ABOUNCE_BUFSIZE VSTREAM_BUFSIZE
238
239 /*
240 * We share most of the verp and non-verp code paths.
241 */
242 #define ABOUNCE_NO_VERP ((char *) 0)
243
244 /*
245 * SLMs.
246 */
247 #define STR(x) vstring_str(x)
248 #define LEN(x) VSTRING_LEN(x)
249
250 /* abounce_done - deliver status to application and clean up pseudo thread */
251
abounce_done(ABOUNCE_STATE * ap,int status)252 static void abounce_done(ABOUNCE_STATE *ap, int status)
253 {
254 if (ap->fp) {
255 event_disable_readwrite(vstream_fileno(ap->fp));
256 (void) vstream_fclose(ap->fp);
257 }
258 if (status != 0 && (ap->flags & BOUNCE_FLAG_CLEAN) == 0)
259 msg_info("%s: status=deferred (%s failed)", ap->id,
260 ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
261 ap->command == BOUNCE_CMD_WARN ? "delay warning" :
262 ap->command == BOUNCE_CMD_VERP ? "verp" :
263 ap->command == BOUNCE_CMD_TRACE ? "trace" :
264 "whatever");
265 ap->callback(status, ap->context);
266 myfree(ap->id);
267 vstring_free(ap->request);
268 myfree((void *) ap);
269 }
270
271 /* abounce_receive - receive server reply */
272
abounce_receive(int event,void * context)273 static void abounce_receive(int event, void *context)
274 {
275 ABOUNCE_STATE *ap = (ABOUNCE_STATE *) context;
276 int status;
277
278 if (event != EVENT_TIME)
279 event_cancel_timer(abounce_receive, context);
280
281 if (event == EVENT_READ
282 && attr_scan(ap->fp, ATTR_FLAG_STRICT,
283 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
284 ATTR_TYPE_END) == 1) {
285 abounce_done(ap, status);
286 } else {
287 abounce_done(ap, -1);
288 }
289 }
290
291 /* abounce_send - send the request and suspend until the server replies */
292
abounce_send(int event,void * context)293 static void abounce_send(int event, void *context)
294 {
295 ABOUNCE_STATE *ap = (ABOUNCE_STATE *) context;
296
297 /*
298 * Receive the server's protocol name announcement. At this point the
299 * server is ready to receive a request without blocking the sender. Send
300 * the request and suspend until the server replies (or dies).
301 */
302 if (event != EVENT_TIME)
303 event_cancel_timer(abounce_send, context);
304
305 non_blocking(vstream_fileno(ap->fp), BLOCKING);
306 if (event == EVENT_READ
307 && attr_scan(ap->fp, ATTR_FLAG_STRICT,
308 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_BOUNCE),
309 ATTR_TYPE_END) == 0
310 && vstream_fwrite(ap->fp, STR(ap->request),
311 LEN(ap->request)) == LEN(ap->request)
312 && vstream_fflush(ap->fp) == 0) {
313 ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_receive,
314 (void *) ap, ABOUNCE_TIMEOUT);
315 } else {
316 abounce_done(ap, -1);
317 }
318 }
319
320 /* abounce_connect - connect and suspend until the server replies */
321
abounce_connect(const char * class,const char * service,int command,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,ABOUNCE_FN callback,void * context)322 static void abounce_connect(const char *class, const char *service,
323 int command, int flags,
324 const char *queue, const char *id,
325 const char *encoding, int smtputf8,
326 const char *sender,
327 const char *dsn_envid, int dsn_ret,
328 const char *verp, ABOUNCE_FN callback,
329 void *context)
330 {
331 ABOUNCE_STATE *ap;
332
333 /*
334 * Save pseudo thread state. Connect to the server. Prior to Postfix 3.6
335 * the asynchronous bounce flush/warn client called mail_connect_wait()
336 * which sleeps and retries several times before terminating with a fatal
337 * error. This block-and-sleep behavior was not consistent with a) the
338 * rest of the code in this module, and with b) the synchronous bounce
339 * client which gives up immediately. It should be safe to give up
340 * immediately because that leaves the bounce/defer/trace logs in the
341 * queue. In particular, this should not increase the simultaneous number
342 * of asynchronous bounce/defer/trace flush/warn requests that are in
343 * flight.
344 */
345 ap = (ABOUNCE_STATE *) mymalloc(sizeof(*ap));
346 ap->command = command;
347 ap->flags = flags;
348 ap->id = mystrdup(id);
349 ap->request = vstring_alloc(ABOUNCE_BUFSIZE);
350 ap->callback = callback;
351 ap->context = context;
352 ap->fp = mail_connect(class, service, NON_BLOCKING);
353
354 /*
355 * Format the request now, so that we don't have to save a lot of
356 * arguments now and format the request later.
357 */
358 if (ap->fp != 0) {
359 /* Note: all code paths must terminate or enable I/O events. */
360 VSTREAM *mp = vstream_memopen(ap->request, O_WRONLY);
361
362 if (attr_print(mp, ATTR_FLAG_MORE,
363 SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
364 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
365 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
366 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
367 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
368 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
369 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
370 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
371 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
372 ATTR_TYPE_END) != 0
373 || (verp != 0
374 && attr_print(mp, ATTR_FLAG_MORE,
375 SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp),
376 ATTR_TYPE_END) != 0)
377 || attr_print(mp, ATTR_FLAG_NONE,
378 ATTR_TYPE_END) != 0
379 || vstream_fclose(mp) != 0)
380 msg_panic("abounce_connect: write request to memory stream: %m");
381
382 /*
383 * Suspend until the server replies (or dies).
384 */
385 ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_send,
386 (void *) ap, ABOUNCE_TIMEOUT);
387 } else {
388 abounce_done(ap, -1);
389 }
390 }
391
392 /* abounce_flush_verp - asynchronous bounce flush */
393
abounce_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,ABOUNCE_FN callback,void * context)394 void abounce_flush_verp(int flags, const char *queue, const char *id,
395 const char *encoding, int smtputf8,
396 const char *sender, const char *dsn_envid,
397 int dsn_ret, const char *verp,
398 ABOUNCE_FN callback,
399 void *context)
400 {
401 abounce_connect(MAIL_CLASS_PRIVATE, var_bounce_service,
402 BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
403 sender, dsn_envid, dsn_ret, verp, callback, context);
404 }
405
406 /* adefer_flush_verp - asynchronous defer flush */
407
adefer_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,ABOUNCE_FN callback,void * context)408 void adefer_flush_verp(int flags, const char *queue, const char *id,
409 const char *encoding, int smtputf8,
410 const char *sender, const char *dsn_envid,
411 int dsn_ret, const char *verp,
412 ABOUNCE_FN callback, void *context)
413 {
414 flags |= BOUNCE_FLAG_DELRCPT;
415 abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service,
416 BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
417 sender, dsn_envid, dsn_ret, verp, callback, context);
418 }
419
420 /* abounce_flush - asynchronous bounce flush */
421
abounce_flush(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,ABOUNCE_FN callback,void * context)422 void abounce_flush(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, ABOUNCE_FN callback,
426 void *context)
427 {
428 abounce_connect(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
429 flags, queue, id, encoding, smtputf8, sender, dsn_envid,
430 dsn_ret, ABOUNCE_NO_VERP, callback, context);
431 }
432
433 /* adefer_flush - asynchronous defer flush */
434
adefer_flush(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,ABOUNCE_FN callback,void * context)435 void adefer_flush(int flags, const char *queue, const char *id,
436 const char *encoding, int smtputf8,
437 const char *sender, const char *dsn_envid,
438 int dsn_ret, ABOUNCE_FN callback, void *context)
439 {
440 flags |= BOUNCE_FLAG_DELRCPT;
441 abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
442 flags, queue, id, encoding, smtputf8, sender, dsn_envid,
443 dsn_ret, ABOUNCE_NO_VERP, callback, context);
444 }
445
446 /* adefer_warn - send copy of defer log to sender as warning bounce */
447
adefer_warn(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,ABOUNCE_FN callback,void * context)448 void adefer_warn(int flags, const char *queue, const char *id,
449 const char *encoding, int smtputf8,
450 const char *sender, const char *dsn_envid,
451 int dsn_ret, ABOUNCE_FN callback, void *context)
452 {
453 abounce_connect(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
454 flags, queue, id, encoding, smtputf8, sender, dsn_envid,
455 dsn_ret, ABOUNCE_NO_VERP, callback, context);
456 }
457
458 /* atrace_flush - asynchronous trace flush */
459
atrace_flush(int flags,const char * queue,const char * id,const char * encoding,int smtputf8,const char * sender,const char * dsn_envid,int dsn_ret,ABOUNCE_FN callback,void * context)460 void atrace_flush(int flags, const char *queue, const char *id,
461 const char *encoding, int smtputf8,
462 const char *sender, const char *dsn_envid,
463 int dsn_ret, ABOUNCE_FN callback, void *context)
464 {
465 abounce_connect(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
466 flags, queue, id, encoding, smtputf8, sender, dsn_envid,
467 dsn_ret, ABOUNCE_NO_VERP, callback, context);
468 }
469