1 /* $NetBSD: deliver_request.c,v 1.4 2025/02/25 19:15:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* deliver_request 3 6 /* SUMMARY 7 /* mail delivery request protocol, server side 8 /* SYNOPSIS 9 /* #include <deliver_request.h> 10 /* 11 /* typedef struct DELIVER_REQUEST { 12 /* .in +5 13 /* VSTREAM *fp; 14 /* int flags; 15 /* char *queue_name; 16 /* char *queue_id; 17 /* long data_offset; 18 /* long data_size; 19 /* char *nexthop; 20 /* char *encoding; 21 /* int sendopts; 22 /* char *sender; 23 /* MSG_STATS msg_stats; 24 /* RECIPIENT_LIST rcpt_list; 25 /* DSN *hop_status; 26 /* char *client_name; 27 /* char *client_addr; 28 /* char *client_port; 29 /* char *client_proto; 30 /* char *client_helo; 31 /* char *sasl_method; 32 /* char *sasl_username; 33 /* char *sasl_sender; 34 /* char *log_ident; 35 /* char *rewrite_context; 36 /* char *dsn_envid; 37 /* int dsn_ret; 38 /* .in -5 39 /* } DELIVER_REQUEST; 40 /* 41 /* DELIVER_REQUEST *deliver_request_read(stream) 42 /* VSTREAM *stream; 43 /* 44 /* void deliver_request_done(stream, request, status) 45 /* VSTREAM *stream; 46 /* DELIVER_REQUEST *request; 47 /* int status; 48 /* DESCRIPTION 49 /* This module implements the delivery agent side of the `queue manager 50 /* to delivery agent' protocol. In this game, the queue manager is 51 /* the client, while the delivery agent is the server. 52 /* 53 /* deliver_request_read() reads a client message delivery request, 54 /* opens the queue file, and acquires a shared lock. 55 /* A null result means that the client sent bad information or that 56 /* it went away unexpectedly. 57 /* 58 /* The \fBflags\fR structure member is the bit-wise OR of zero or more 59 /* of the following: 60 /* .IP \fBDEL_REQ_FLAG_SUCCESS\fR 61 /* Delete successful recipients from the queue file. 62 /* 63 /* Note: currently, this also controls whether bounced recipients 64 /* are deleted. 65 /* 66 /* Note: the deliver_completed() function ignores this request 67 /* when the recipient queue file offset is -1. 68 /* .IP \fBDEL_REQ_FLAG_BOUNCE\fR 69 /* Delete bounced recipients from the queue file. Currently, 70 /* this flag is non-functional. 71 /* .PP 72 /* The \fBDEL_REQ_FLAG_DEFLT\fR constant provides a convenient shorthand 73 /* for the most common case: delete successful and bounced recipients. 74 /* 75 /* The \fIhop_status\fR member must be updated by the caller 76 /* when all delivery to the destination in \fInexthop\fR should 77 /* be deferred. This member is passed to dsn_free(). 78 /* 79 /* deliver_request_done() reports the delivery status back to the 80 /* client, including the optional \fIhop_status\fR etc. information, 81 /* closes the queue file, 82 /* and destroys the DELIVER_REQUEST structure. The result is 83 /* non-zero when the status could not be reported to the client. 84 /* DIAGNOSTICS 85 /* Warnings: bad data sent by the client. Fatal errors: out of 86 /* memory, queue file open errors. 87 /* SEE ALSO 88 /* attr_scan(3) low-level intra-mail input routines 89 /* LICENSE 90 /* .ad 91 /* .fi 92 /* The Secure Mailer license must be distributed with this software. 93 /* AUTHOR(S) 94 /* Wietse Venema 95 /* IBM T.J. Watson Research 96 /* P.O. Box 704 97 /* Yorktown Heights, NY 10598, USA 98 /* 99 /* Wietse Venema 100 /* Google, Inc. 101 /* 111 8th Avenue 102 /* New York, NY 10011, USA 103 /* 104 /* Wietse Venema 105 /* porcupine.org 106 /*--*/ 107 108 /* System library. */ 109 110 #include <sys_defs.h> 111 #include <sys/stat.h> 112 #include <string.h> 113 #include <unistd.h> 114 #include <errno.h> 115 116 /* Utility library. */ 117 118 #include <msg.h> 119 #include <vstream.h> 120 #include <vstring.h> 121 #include <mymalloc.h> 122 #include <iostuff.h> 123 #include <myflock.h> 124 125 /* Global library. */ 126 127 #include "mail_queue.h" 128 #include "mail_proto.h" 129 #include "mail_open_ok.h" 130 #include "recipient_list.h" 131 #include "dsn.h" 132 #include "dsn_print.h" 133 #include "deliver_request.h" 134 #include "rcpt_buf.h" 135 136 /* deliver_request_initial - send initial status code */ 137 138 static int deliver_request_initial(VSTREAM *stream) 139 { 140 int err; 141 142 /* 143 * The master processes runs a finite number of delivery agent processes 144 * to handle service requests. Thus, a delivery agent process must send 145 * something to inform the queue manager that it is ready to receive a 146 * delivery request; otherwise the queue manager could block in write(). 147 */ 148 if (msg_verbose) 149 msg_info("deliver_request_initial: send initial response"); 150 attr_print(stream, ATTR_FLAG_NONE, 151 SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_DELIVER), 152 ATTR_TYPE_END); 153 if ((err = vstream_fflush(stream)) != 0) 154 if (msg_verbose) 155 msg_warn("send initial response: %m"); 156 return (err); 157 } 158 159 /* deliver_request_final - send final delivery request status */ 160 161 static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request, 162 int status) 163 { 164 DSN *hop_status; 165 int err; 166 167 /* XXX This DSN structure initialization bypasses integrity checks. */ 168 static DSN dummy_dsn = {"", "", "", "", "", "", ""}; 169 170 /* 171 * Send the status and the optional reason. 172 */ 173 if ((hop_status = request->hop_status) == 0) 174 hop_status = &dummy_dsn; 175 if (msg_verbose) 176 msg_info("deliver_request_final: send: \"%s\" %d", 177 hop_status->reason, status); 178 attr_print(stream, ATTR_FLAG_NONE, 179 SEND_ATTR_FUNC(dsn_print, (const void *) hop_status), 180 SEND_ATTR_INT(MAIL_ATTR_STATUS, status), 181 ATTR_TYPE_END); 182 if ((err = vstream_fflush(stream)) != 0) 183 if (msg_verbose) 184 msg_warn("send final status: %m"); 185 186 /* 187 * With some UNIX systems, stream sockets lose data when you close them 188 * immediately after writing to them. That is not how sockets are 189 * supposed to behave! The workaround is to wait until the receiver 190 * closes the connection. Calling VSTREAM_GETC() has the benefit of using 191 * whatever timeout is specified in the ipc_timeout parameter. 192 */ 193 (void) VSTREAM_GETC(stream); 194 return (err); 195 } 196 197 /* deliver_request_get - receive message delivery request */ 198 199 static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) 200 { 201 const char *myname = "deliver_request_get"; 202 const char *path; 203 struct stat st; 204 static VSTRING *queue_name; 205 static VSTRING *queue_id; 206 static VSTRING *nexthop; 207 static VSTRING *encoding; 208 static VSTRING *address; 209 static VSTRING *client_name; 210 static VSTRING *client_addr; 211 static VSTRING *client_port; 212 static VSTRING *client_proto; 213 static VSTRING *client_helo; 214 static VSTRING *sasl_method; 215 static VSTRING *sasl_username; 216 static VSTRING *sasl_sender; 217 static VSTRING *log_ident; 218 static VSTRING *rewrite_context; 219 static VSTRING *dsn_envid; 220 static RCPT_BUF *rcpt_buf; 221 int rcpt_count; 222 int sendopts; 223 int dsn_ret; 224 225 /* 226 * Initialize. For some reason I wanted to allow for multiple instances 227 * of a deliver_request structure, thus the hoopla with string 228 * initialization and copying. 229 */ 230 if (queue_name == 0) { 231 queue_name = vstring_alloc(10); 232 queue_id = vstring_alloc(10); 233 nexthop = vstring_alloc(10); 234 encoding = vstring_alloc(10); 235 address = vstring_alloc(10); 236 client_name = vstring_alloc(10); 237 client_addr = vstring_alloc(10); 238 client_port = vstring_alloc(10); 239 client_proto = vstring_alloc(10); 240 client_helo = vstring_alloc(10); 241 sasl_method = vstring_alloc(10); 242 sasl_username = vstring_alloc(10); 243 sasl_sender = vstring_alloc(10); 244 log_ident = vstring_alloc(10); 245 rewrite_context = vstring_alloc(10); 246 dsn_envid = vstring_alloc(10); 247 rcpt_buf = rcpb_create(); 248 } 249 250 /* 251 * Extract the queue file name, data offset, and sender address. Abort 252 * the conversation when they send bad information. 253 */ 254 if (attr_scan(stream, ATTR_FLAG_STRICT, 255 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request->flags), 256 RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), 257 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), 258 RECV_ATTR_LONG(MAIL_ATTR_OFFSET, &request->data_offset), 259 RECV_ATTR_LONG(MAIL_ATTR_SIZE, &request->data_size), 260 RECV_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop), 261 RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding), 262 RECV_ATTR_INT(MAIL_ATTR_SENDOPTS, &sendopts), 263 RECV_ATTR_STR(MAIL_ATTR_SENDER, address), 264 RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), 265 RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret), 266 RECV_ATTR_FUNC(msg_stats_scan, (void *) &request->msg_stats), 267 /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ 268 RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, client_name), 269 RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, client_addr), 270 RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, client_port), 271 RECV_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, client_proto), 272 RECV_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, client_helo), 273 /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ 274 RECV_ATTR_STR(MAIL_ATTR_SASL_METHOD, sasl_method), 275 RECV_ATTR_STR(MAIL_ATTR_SASL_USERNAME, sasl_username), 276 RECV_ATTR_STR(MAIL_ATTR_SASL_SENDER, sasl_sender), 277 /* XXX Ditto if we want to pass TLS certificate info. */ 278 RECV_ATTR_STR(MAIL_ATTR_LOG_IDENT, log_ident), 279 RECV_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, rewrite_context), 280 RECV_ATTR_INT(MAIL_ATTR_RCPT_COUNT, &rcpt_count), 281 ATTR_TYPE_END) != 23) { 282 msg_warn("%s: error receiving common attributes", myname); 283 return (-1); 284 } 285 if (mail_open_ok(vstring_str(queue_name), 286 vstring_str(queue_id), &st, &path) == 0) 287 return (-1); 288 289 /* Don't override hand-off time after deliver_pass() delegation. */ 290 if (request->msg_stats.agent_handoff.tv_sec == 0) 291 GETTIMEOFDAY(&request->msg_stats.agent_handoff); 292 293 request->queue_name = mystrdup(vstring_str(queue_name)); 294 request->queue_id = mystrdup(vstring_str(queue_id)); 295 request->nexthop = mystrdup(vstring_str(nexthop)); 296 request->encoding = mystrdup(vstring_str(encoding)); 297 /* Fix 20140708: dedicated attribute for SMTPUTF8 etc. flags. */ 298 request->sendopts = sendopts; 299 request->sender = mystrdup(vstring_str(address)); 300 request->client_name = mystrdup(vstring_str(client_name)); 301 request->client_addr = mystrdup(vstring_str(client_addr)); 302 request->client_port = mystrdup(vstring_str(client_port)); 303 request->client_proto = mystrdup(vstring_str(client_proto)); 304 request->client_helo = mystrdup(vstring_str(client_helo)); 305 request->sasl_method = mystrdup(vstring_str(sasl_method)); 306 request->sasl_username = mystrdup(vstring_str(sasl_username)); 307 request->sasl_sender = mystrdup(vstring_str(sasl_sender)); 308 request->log_ident = mystrdup(vstring_str(log_ident)); 309 request->rewrite_context = mystrdup(vstring_str(rewrite_context)); 310 request->dsn_envid = mystrdup(vstring_str(dsn_envid)); 311 request->dsn_ret = dsn_ret; 312 313 /* 314 * Extract the recipient offset and address list. Skip over any 315 * attributes from the sender that we do not understand. 316 */ 317 while (rcpt_count-- > 0) { 318 if (attr_scan(stream, ATTR_FLAG_STRICT, 319 RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf), 320 ATTR_TYPE_END) != 1) { 321 msg_warn("%s: error receiving recipient attributes", myname); 322 return (-1); 323 } 324 recipient_list_add(&request->rcpt_list, rcpt_buf->offset, 325 vstring_str(rcpt_buf->dsn_orcpt), 326 rcpt_buf->dsn_notify, 327 vstring_str(rcpt_buf->orig_addr), 328 vstring_str(rcpt_buf->address)); 329 } 330 if (request->rcpt_list.len <= 0) { 331 msg_warn("%s: no recipients in delivery request for destination %s", 332 request->queue_id, request->nexthop); 333 return (-1); 334 } 335 336 /* 337 * Open the queue file and set a shared lock, in order to prevent 338 * duplicate deliveries when the queue is flushed immediately after queue 339 * manager restart. 340 * 341 * The queue manager locks the file exclusively when it enters the active 342 * queue, and releases the lock before starting deliveries from that 343 * file. The queue manager does not lock the file again when reading more 344 * recipients into memory. When the queue manager is restarted, the new 345 * process moves files from the active queue to the incoming queue to 346 * cool off for a while. Delivery agents should therefore never try to 347 * open a file that is locked by a queue manager process. 348 * 349 * Opening the queue file can fail for a variety of reasons, such as the 350 * system running out of resources. Instead of throwing away mail, we're 351 * raising a fatal error which forces the mail system to back off, and 352 * retry later. 353 */ 354 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT) 355 356 request->fp = 357 mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0); 358 if (request->fp == 0) { 359 if (errno != ENOENT) 360 msg_fatal("open %s %s: %m", request->queue_name, request->queue_id); 361 msg_warn("open %s %s: %m", request->queue_name, request->queue_id); 362 return (-1); 363 } 364 if (msg_verbose) 365 msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp)); 366 if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0) 367 msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp)); 368 close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC); 369 370 return (0); 371 } 372 373 /* deliver_request_alloc - allocate delivery request structure */ 374 375 static DELIVER_REQUEST *deliver_request_alloc(void) 376 { 377 DELIVER_REQUEST *request; 378 379 request = (DELIVER_REQUEST *) mymalloc(sizeof(*request)); 380 request->fp = 0; 381 request->queue_name = 0; 382 request->queue_id = 0; 383 request->nexthop = 0; 384 request->encoding = 0; 385 request->sender = 0; 386 request->data_offset = 0; 387 request->data_size = 0; 388 recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS); 389 request->hop_status = 0; 390 request->client_name = 0; 391 request->client_addr = 0; 392 request->client_port = 0; 393 request->client_proto = 0; 394 request->client_helo = 0; 395 request->sasl_method = 0; 396 request->sasl_username = 0; 397 request->sasl_sender = 0; 398 request->log_ident = 0; 399 request->rewrite_context = 0; 400 request->dsn_envid = 0; 401 return (request); 402 } 403 404 /* deliver_request_free - clean up delivery request structure */ 405 406 static void deliver_request_free(DELIVER_REQUEST *request) 407 { 408 if (request->fp) 409 vstream_fclose(request->fp); 410 if (request->queue_name) 411 myfree(request->queue_name); 412 if (request->queue_id) 413 myfree(request->queue_id); 414 if (request->nexthop) 415 myfree(request->nexthop); 416 if (request->encoding) 417 myfree(request->encoding); 418 if (request->sender) 419 myfree(request->sender); 420 recipient_list_free(&request->rcpt_list); 421 if (request->hop_status) 422 dsn_free(request->hop_status); 423 if (request->client_name) 424 myfree(request->client_name); 425 if (request->client_addr) 426 myfree(request->client_addr); 427 if (request->client_port) 428 myfree(request->client_port); 429 if (request->client_proto) 430 myfree(request->client_proto); 431 if (request->client_helo) 432 myfree(request->client_helo); 433 if (request->sasl_method) 434 myfree(request->sasl_method); 435 if (request->sasl_username) 436 myfree(request->sasl_username); 437 if (request->sasl_sender) 438 myfree(request->sasl_sender); 439 if (request->log_ident) 440 myfree(request->log_ident); 441 if (request->rewrite_context) 442 myfree(request->rewrite_context); 443 if (request->dsn_envid) 444 myfree(request->dsn_envid); 445 myfree((void *) request); 446 } 447 448 /* deliver_request_read - create and read delivery request */ 449 450 DELIVER_REQUEST *deliver_request_read(VSTREAM *stream) 451 { 452 DELIVER_REQUEST *request; 453 454 /* 455 * Tell the queue manager that we are ready for this request. 456 */ 457 if (deliver_request_initial(stream) != 0) 458 return (0); 459 460 /* 461 * Be prepared for the queue manager to change its mind after contacting 462 * us. This can happen when a transport or host goes bad. 463 */ 464 (void) read_wait(vstream_fileno(stream), -1); 465 if (peekfd(vstream_fileno(stream)) <= 0) 466 return (0); 467 468 /* 469 * Allocate and read the queue manager's delivery request. 470 */ 471 #define XXX_DEFER_STATUS -1 472 473 request = deliver_request_alloc(); 474 if (deliver_request_get(stream, request) < 0) { 475 deliver_request_done(stream, request, XXX_DEFER_STATUS); 476 request = 0; 477 } 478 return (request); 479 } 480 481 /* deliver_request_done - finish delivery request */ 482 483 int deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status) 484 { 485 int err; 486 487 err = deliver_request_final(stream, request, status); 488 deliver_request_free(request); 489 return (err); 490 } 491