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