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