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 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 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 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 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 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 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 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 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 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 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