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