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